diff options
406 files changed, 9717 insertions, 7562 deletions
diff --git a/Android.bp b/Android.bp index c6f9362b4919..59530079d4ad 100644 --- a/Android.bp +++ b/Android.bp @@ -547,6 +547,7 @@ java_library { exclude_srcs: ["core/java/android/content/pm/AndroidTestBaseUpdater.java"], aidl: { generate_get_transaction_name: true, + local_include_dirs: ["media/aidl"], }, dxflags: [ "--core-library", @@ -583,6 +584,7 @@ java_library { // in favor of an API stubs dependency in java_library "framework" below. "mimemap", "mediatranscoding_aidl_interface-java", + "soundtrigger_middleware-aidl-java", ], // For backwards compatibility. stem: "framework", diff --git a/ApiDocs.bp b/ApiDocs.bp index ca921ff97c35..c82fee0fe8ac 100644 --- a/ApiDocs.bp +++ b/ApiDocs.bp @@ -83,6 +83,10 @@ stubs_defaults { merge_annotations_dirs: [ "metalava-manual", ], + // TODO(b/169090544): remove below aidl includes. + aidl: { + local_include_dirs: ["media/aidl"], + }, } droidstubs { @@ -150,6 +154,10 @@ doc_defaults { ":current-support-api", ":current-androidx-api", ], + // TODO(b/169090544): remove below aidl includes. + aidl: { + local_include_dirs: ["media/aidl"], + }, } doc_defaults { diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg index fc5efc6e03ac..7a8d1a1caf25 100644 --- a/PREUPLOAD.cfg +++ b/PREUPLOAD.cfg @@ -11,6 +11,7 @@ clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp libs/input/ services/core/jni/ services/incremental/ + tools/ [Hook Scripts] checkstyle_hook = ${REPO_ROOT}/prebuilts/checkstyle/checkstyle.py --sha ${PREUPLOAD_COMMIT} diff --git a/StubLibraries.bp b/StubLibraries.bp index bb6538739c49..852fcc6128c4 100644 --- a/StubLibraries.bp +++ b/StubLibraries.bp @@ -51,9 +51,12 @@ stubs_defaults { ":android_icu4j_public_api_files", "**/package.html", ], - // TODO(b/147699819): remove below aidl includes. + // TODO(b/147699819, b/169090544): remove below aidl includes. aidl: { - local_include_dirs: ["telephony/java"], + local_include_dirs: [ + "telephony/java", + "media/aidl", + ], }, libs: ["framework-internal-utils"], installable: false, @@ -488,29 +491,3 @@ java_library_static { ":hwbinder-stubs-docs", ], } - -///////////////////////////////////////////////////////////////////// -// api/*-current.txt files for use by modules in other directories -// like the CTS test -///////////////////////////////////////////////////////////////////// - -filegroup { - name: "frameworks-base-api-current.txt", - srcs: [ - "api/current.txt", - ], -} - -filegroup { - name: "frameworks-base-api-system-current.txt", - srcs: [ - "api/system-current.txt", - ], -} - -filegroup { - name: "frameworks-base-api-system-removed.txt", - srcs: [ - "api/system-removed.txt", - ], -} diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java index b638fef36a9e..4512d77e650e 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java @@ -2590,13 +2590,14 @@ public class JobSchedulerService extends com.android.server.SystemService // job that runs one of the app's services, as well as verifying that the // named service properly requires the BIND_JOB_SERVICE permission private void enforceValidJobRequest(int uid, JobInfo job) { - final IPackageManager pm = AppGlobals.getPackageManager(); + final PackageManager pm = getContext() + .createContextAsUser(UserHandle.getUserHandleForUid(uid), 0) + .getPackageManager(); final ComponentName service = job.getService(); try { ServiceInfo si = pm.getServiceInfo(service, PackageManager.MATCH_DIRECT_BOOT_AWARE - | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, - UserHandle.getUserId(uid)); + | PackageManager.MATCH_DIRECT_BOOT_UNAWARE); if (si == null) { throw new IllegalArgumentException("No such service " + service); } @@ -2608,8 +2609,10 @@ public class JobSchedulerService extends com.android.server.SystemService throw new IllegalArgumentException("Scheduled service " + service + " does not require android.permission.BIND_JOB_SERVICE permission"); } - } catch (RemoteException e) { - // Can't happen; the Package Manager is in this same process + } catch (NameNotFoundException e) { + throw new IllegalArgumentException( + "Tried to schedule job for non-existent package: " + + service.getPackageName()); } } diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/ComponentController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/ComponentController.java index 47ebf32fa875..999c53fb7daf 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/controllers/ComponentController.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/ComponentController.java @@ -18,16 +18,15 @@ package com.android.server.job.controllers; import android.annotation.NonNull; import android.annotation.Nullable; -import android.app.AppGlobals; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ServiceInfo; import android.net.Uri; -import android.os.RemoteException; import android.os.UserHandle; import android.util.IndentingPrintWriter; import android.util.Log; @@ -116,10 +115,15 @@ public class ComponentController extends StateController { ServiceInfo si = mServiceInfoCache.get(userId, service); if (si == null) { try { - si = AppGlobals.getPackageManager().getServiceInfo( - service, PackageManager.MATCH_DIRECT_BOOT_AUTO, userId); - } catch (RemoteException e) { - throw new RuntimeException(e); + // createContextAsUser may potentially be expensive + // TODO: cache user context or improve ContextImpl implementation if this becomes + // a problem + si = mContext.createContextAsUser(UserHandle.of(userId), 0) + .getPackageManager() + .getServiceInfo(service, PackageManager.MATCH_DIRECT_BOOT_AUTO); + } catch (NameNotFoundException e) { + Slog.e(TAG, "Job exists for non-existent package: " + service.getPackageName()); + return null; } mServiceInfoCache.add(userId, service, si); } diff --git a/api/Android.bp b/api/Android.bp index 54ff82c97e17..cb6d448caf63 100644 --- a/api/Android.bp +++ b/api/Android.bp @@ -1,7 +1,97 @@ +// 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 { + default_visibility: ["//visibility:private"], +} + +// *-current.txt files for use by modules in other directories like cts +filegroup { + name: "frameworks-base-api-current.txt", + srcs: ["current.txt"], + visibility: ["//visibility:public"], +} + +filegroup { + name: "frameworks-base-api-system-current.txt", + srcs: ["system-current.txt"], + visibility: ["//visibility:public"], +} + +filegroup { + name: "frameworks-base-api-system-removed.txt", + srcs: ["system-removed.txt"], + visibility: ["//visibility:public"], +} + genrule { name: "current-api-xml", tools: ["metalava"], srcs: ["current.txt"], out: ["current.api"], cmd: "$(location metalava) --no-banner -convert2xmlnostrip $(in) $(out)", + visibility: ["//visibility:public"], +} + +genrule { + name: "frameworks-base-api-current-merged.txt", + srcs: [ + ":conscrypt.module.public.api{.public.api.txt}", + ":framework-media{.public.api.txt}", + ":framework-mediaprovider{.public.api.txt}", + ":framework-permission{.public.api.txt}", + ":framework-sdkextensions{.public.api.txt}", + ":framework-statsd{.public.api.txt}", + ":framework-tethering{.public.api.txt}", + ":framework-wifi{.public.api.txt}", + ":non-updatable-current.txt", + ], + out: ["current.txt"], + tools: ["metalava"], + cmd: "$(location metalava) --no-banner --format=v2 $(in) --api $(out)", +} + +genrule { + name: "frameworks-base-api-system-current-merged.txt", + srcs: [ + ":framework-media{.system.api.txt}", + ":framework-mediaprovider{.system.api.txt}", + ":framework-permission{.system.api.txt}", + ":framework-sdkextensions{.system.api.txt}", + ":framework-statsd{.system.api.txt}", + ":framework-tethering{.system.api.txt}", + ":framework-wifi{.system.api.txt}", + ":non-updatable-system-current.txt", + ], + out: ["system-current.txt"], + tools: ["metalava"], + cmd: "$(location metalava) --no-banner --format=v2 $(in) --api $(out)", +} + +genrule { + name: "frameworks-base-api-module-lib-current-merged.txt", + srcs: [ + ":framework-media{.module-lib.api.txt}", + ":framework-mediaprovider{.module-lib.api.txt}", + ":framework-permission{.module-lib.api.txt}", + ":framework-sdkextensions{.module-lib.api.txt}", + ":framework-statsd{.module-lib.api.txt}", + ":framework-tethering{.module-lib.api.txt}", + ":framework-wifi{.module-lib.api.txt}", + ":non-updatable-module-lib-current.txt", + ], + out: ["module-lib-current.txt"], + tools: ["metalava"], + cmd: "$(location metalava) --no-banner --format=v2 $(in) --api $(out)", } diff --git a/api/current.txt b/api/current.txt index 4590325cb771..c0dbdef4ab42 100644 --- a/api/current.txt +++ b/api/current.txt @@ -6138,6 +6138,7 @@ package android.app { field @NonNull public static final android.os.Parcelable.Creator<android.app.PendingIntent> CREATOR; field public static final int FLAG_CANCEL_CURRENT = 268435456; // 0x10000000 field public static final int FLAG_IMMUTABLE = 67108864; // 0x4000000 + field public static final int FLAG_MUTABLE = 33554432; // 0x2000000 field public static final int FLAG_NO_CREATE = 536870912; // 0x20000000 field public static final int FLAG_ONE_SHOT = 1073741824; // 0x40000000 field public static final int FLAG_UPDATE_CURRENT = 134217728; // 0x8000000 @@ -14292,6 +14293,7 @@ package android.graphics { public final class BlurShader extends android.graphics.Shader { ctor public BlurShader(float, float, @Nullable android.graphics.Shader); + ctor public BlurShader(float, float, @Nullable android.graphics.Shader, @NonNull android.graphics.Shader.TileMode); } public class Camera { @@ -15195,6 +15197,20 @@ package android.graphics { ctor public PaintFlagsDrawFilter(int, int); } + public final class ParcelableColorSpace extends android.graphics.ColorSpace implements android.os.Parcelable { + ctor public ParcelableColorSpace(@NonNull android.graphics.ColorSpace); + method public int describeContents(); + method @NonNull public float[] fromXyz(@NonNull float[]); + method @NonNull public android.graphics.ColorSpace getColorSpace(); + method public float getMaxValue(int); + method public float getMinValue(int); + method public static boolean isParcelable(@NonNull android.graphics.ColorSpace); + method public boolean isWideGamut(); + method @NonNull public float[] toXyz(@NonNull float[]); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.graphics.ParcelableColorSpace> CREATOR; + } + public class Path { ctor public Path(); ctor public Path(@Nullable android.graphics.Path); @@ -15630,6 +15646,7 @@ package android.graphics { public enum Shader.TileMode { enum_constant public static final android.graphics.Shader.TileMode CLAMP; + enum_constant public static final android.graphics.Shader.TileMode DECAL; enum_constant public static final android.graphics.Shader.TileMode MIRROR; enum_constant public static final android.graphics.Shader.TileMode REPEAT; } @@ -24913,6 +24930,10 @@ package android.media { method public double getAttributeDouble(@NonNull String, double); method public int getAttributeInt(@NonNull String, int); method @Nullable public long[] getAttributeRange(@NonNull String); + method public long getDateTime(); + method public long getDateTimeDigitized(); + method public long getDateTimeOriginal(); + method public long getGpsDateTime(); method public boolean getLatLong(float[]); method public byte[] getThumbnail(); method public android.graphics.Bitmap getThumbnailBitmap(); @@ -32009,9 +32030,15 @@ package android.net.wifi.hotspot2.pps { method public int describeContents(); method public String getFqdn(); method public String getFriendlyName(); + method @Nullable public long[] getMatchAllOis(); + method @Nullable public long[] getMatchAnyOis(); + method @Nullable public String[] getOtherHomePartners(); method public long[] getRoamingConsortiumOis(); method public void setFqdn(String); method public void setFriendlyName(String); + method public void setMatchAllOis(@Nullable long[]); + method public void setMatchAnyOis(@Nullable long[]); + method public void setOtherHomePartners(@Nullable String[]); method public void setRoamingConsortiumOis(long[]); method public void writeToParcel(android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.hotspot2.pps.HomeSp> CREATOR; diff --git a/api/module-lib-current.txt b/api/module-lib-current.txt index ab8944277e9a..d27f5a5c006e 100644 --- a/api/module-lib-current.txt +++ b/api/module-lib-current.txt @@ -66,6 +66,7 @@ package android.media.session { } public final class MediaSessionManager { + method public void addOnActiveSessionsChangedListener(@NonNull android.media.session.MediaSessionManager.OnActiveSessionsChangedListener, @Nullable android.content.ComponentName, int, @Nullable android.os.Handler); method public void dispatchMediaKeyEventAsSystemService(@NonNull android.view.KeyEvent); method public boolean dispatchMediaKeyEventAsSystemService(@NonNull android.media.session.MediaSession.Token, @NonNull android.view.KeyEvent); method public void dispatchVolumeKeyEventAsSystemService(@NonNull android.view.KeyEvent, int); diff --git a/api/system-current.txt b/api/system-current.txt index c4f9067b507d..118184df9d00 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -4399,6 +4399,8 @@ package android.media { } public static final class MediaTranscodeManager.TranscodingRequest { + method public int getClientPid(); + method public int getClientUid(); method @NonNull public android.net.Uri getDestinationUri(); method public int getPriority(); method @NonNull public android.net.Uri getSourceUri(); @@ -4409,6 +4411,8 @@ package android.media { public static final class MediaTranscodeManager.TranscodingRequest.Builder { ctor public MediaTranscodeManager.TranscodingRequest.Builder(); method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest build(); + method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setClientPid(int); + method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setClientUid(int); method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setDestinationUri(@NonNull android.net.Uri); method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setPriority(int); method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setSourceUri(@NonNull android.net.Uri); @@ -7424,7 +7428,7 @@ package android.net.wifi { field public static final int EASY_CONNECT_NETWORK_ROLE_AP = 1; // 0x1 field public static final int EASY_CONNECT_NETWORK_ROLE_STA = 0; // 0x0 field public static final String EXTRA_CHANGE_REASON = "changeReason"; - field public static final String EXTRA_LINK_PROPERTIES = "android.net.wifi.extra.LINK_PROPERTIES"; + field @Deprecated public static final String EXTRA_LINK_PROPERTIES = "android.net.wifi.extra.LINK_PROPERTIES"; field public static final String EXTRA_MULTIPLE_NETWORKS_CHANGED = "multipleChanges"; field public static final String EXTRA_OSU_NETWORK = "android.net.wifi.extra.OSU_NETWORK"; field public static final String EXTRA_PREVIOUS_WIFI_AP_STATE = "previous_wifi_state"; diff --git a/api/test-current.txt b/api/test-current.txt index 5082c57cd221..128e84f59048 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -997,6 +997,7 @@ package android.content.pm { method public void setEnableRollback(boolean, int); method @RequiresPermission("android.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS") public void setGrantedRuntimePermissions(String[]); method @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES) public void setInstallAsApex(); + method public void setInstallAsInstantApp(boolean); method public void setInstallerPackageName(@Nullable String); method public void setRequestDowngrade(boolean); method @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES) public void setStaged(); @@ -1852,6 +1853,8 @@ package android.media { } public static final class MediaTranscodeManager.TranscodingRequest { + method public int getClientPid(); + method public int getClientUid(); method @NonNull public android.net.Uri getDestinationUri(); method public int getPriority(); method @NonNull public android.net.Uri getSourceUri(); @@ -1862,6 +1865,8 @@ package android.media { public static final class MediaTranscodeManager.TranscodingRequest.Builder { ctor public MediaTranscodeManager.TranscodingRequest.Builder(); method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest build(); + method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setClientPid(int); + method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setClientUid(int); method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setDestinationUri(@NonNull android.net.Uri); method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setPriority(int); method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setSourceUri(@NonNull android.net.Uri); diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp index 9c796128df84..046145f90808 100644 --- a/cmds/bootanimation/BootAnimation.cpp +++ b/cmds/bootanimation/BootAnimation.cpp @@ -482,6 +482,8 @@ status_t BootAnimation::readyToRun() { mFlingerSurface = s; mTargetInset = -1; + projectSceneToWindow(); + // Register a display event receiver mDisplayEventReceiver = std::make_unique<DisplayEventReceiver>(); status_t status = mDisplayEventReceiver->initCheck(); @@ -493,6 +495,16 @@ status_t BootAnimation::readyToRun() { return NO_ERROR; } +void BootAnimation::projectSceneToWindow() { + glViewport(0, 0, mWidth, mHeight); + glScissor(0, 0, mWidth, mHeight); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrthof(0, static_cast<float>(mWidth), 0, static_cast<float>(mHeight), -1, 1); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); +} + void BootAnimation::resizeSurface(int newWidth, int newHeight) { // We assume this function is called on the animation thread. if (newWidth == mWidth && newHeight == mHeight) { @@ -517,8 +529,8 @@ void BootAnimation::resizeSurface(int newWidth, int newHeight) { SLOGE("Can't make the new surface current. Error %d", eglGetError()); return; } - glViewport(0, 0, mWidth, mHeight); - glScissor(0, 0, mWidth, mHeight); + + projectSceneToWindow(); mSurface = surface; } @@ -799,6 +811,37 @@ status_t BootAnimation::initFont(Font* font, const char* fallback) { return status; } +void BootAnimation::fadeFrame(const int frameLeft, const int frameBottom, const int frameWidth, + const int frameHeight, const Animation::Part& part, + const int fadedFramesCount) { + glEnable(GL_BLEND); + glEnableClientState(GL_VERTEX_ARRAY); + glDisable(GL_TEXTURE_2D); + // avoid creating a hole due to mixing result alpha with GL_REPLACE texture + glBlendFuncSeparateOES(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ZERO, GL_ONE); + + const float alpha = static_cast<float>(fadedFramesCount) / part.framesToFadeCount; + glColor4f(part.backgroundColor[0], part.backgroundColor[1], part.backgroundColor[2], alpha); + + const float frameStartX = static_cast<float>(frameLeft); + const float frameStartY = static_cast<float>(frameBottom); + const float frameEndX = frameStartX + frameWidth; + const float frameEndY = frameStartY + frameHeight; + const GLfloat frameRect[] = { + frameStartX, frameStartY, + frameEndX, frameStartY, + frameEndX, frameEndY, + frameStartX, frameEndY + }; + glVertexPointer(2, GL_FLOAT, 0, frameRect); + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glEnable(GL_TEXTURE_2D); + glDisableClientState(GL_VERTEX_ARRAY); + glDisable(GL_BLEND); +} + void BootAnimation::drawText(const char* str, const Font& font, bool bold, int* x, int* y) { glEnable(GL_BLEND); // Allow us to draw on top of the animation glBindTexture(GL_TEXTURE_2D, font.texture.name); @@ -890,23 +933,34 @@ bool BootAnimation::parseAnimationDesc(Animation& animation) { int height = 0; int count = 0; int pause = 0; + int framesToFadeCount = 0; char path[ANIM_ENTRY_NAME_MAX]; char color[7] = "000000"; // default to black if unspecified char clockPos1[TEXT_POS_LEN_MAX + 1] = ""; char clockPos2[TEXT_POS_LEN_MAX + 1] = ""; - char pathType; + + int nextReadPos; + if (sscanf(l, "%d %d %d", &width, &height, &fps) == 3) { // SLOGD("> w=%d, h=%d, fps=%d", width, height, fps); animation.width = width; animation.height = height; animation.fps = fps; - } else if (sscanf(l, " %c %d %d %" STRTO(ANIM_PATH_MAX) "s #%6s %16s %16s", - &pathType, &count, &pause, path, color, clockPos1, clockPos2) >= 4) { - //SLOGD("> type=%c, count=%d, pause=%d, path=%s, color=%s, clockPos1=%s, clockPos2=%s", - // pathType, count, pause, path, color, clockPos1, clockPos2); + } else if (sscanf(l, "%c %d %d %" STRTO(ANIM_PATH_MAX) "s%n", + &pathType, &count, &pause, path, &nextReadPos) >= 4) { + if (pathType == 'f') { + sscanf(l + nextReadPos, " %d #%6s %16s %16s", &framesToFadeCount, color, clockPos1, + clockPos2); + } else { + sscanf(l + nextReadPos, " #%6s %16s %16s", color, clockPos1, clockPos2); + } + // SLOGD("> type=%c, count=%d, pause=%d, path=%s, framesToFadeCount=%d, color=%s, " + // "clockPos1=%s, clockPos2=%s", + // pathType, count, pause, path, framesToFadeCount, color, clockPos1, clockPos2); Animation::Part part; part.playUntilComplete = pathType == 'c'; + part.framesToFadeCount = framesToFadeCount; part.count = count; part.pause = pause; part.path = path; @@ -925,6 +979,7 @@ bool BootAnimation::parseAnimationDesc(Animation& animation) { // SLOGD("> SYSTEM"); Animation::Part part; part.playUntilComplete = false; + part.framesToFadeCount = 0; part.count = 1; part.pause = 0; part.audioData = nullptr; @@ -1121,12 +1176,19 @@ bool BootAnimation::movie() { return false; } +bool BootAnimation::shouldStopPlayingPart(const Animation::Part& part, const int fadedFramesCount) { + // stop playing only if it is time to exit and it's a partial part which has been faded out + return exitPending() && !part.playUntilComplete && fadedFramesCount >= part.framesToFadeCount; +} + bool BootAnimation::playAnimation(const Animation& animation) { const size_t pcount = animation.parts.size(); nsecs_t frameDuration = s2ns(1) / animation.fps; SLOGD("%sAnimationShownTiming start time: %" PRId64 "ms", mShuttingDown ? "Shutdown" : "Boot", elapsedRealtime()); + + int fadedFramesCount = 0; for (size_t i=0 ; i<pcount ; i++) { const Animation::Part& part(animation.parts[i]); const size_t fcount = part.frames.size(); @@ -1140,10 +1202,9 @@ bool BootAnimation::playAnimation(const Animation& animation) { continue; //to next part } - for (int r=0 ; !part.count || r<part.count ; r++) { - // Exit any non playuntil complete parts immediately - if(exitPending() && !part.playUntilComplete) - break; + // process the part not only while the count allows but also if already fading + for (int r=0 ; !part.count || r<part.count || fadedFramesCount > 0 ; r++) { + if (shouldStopPlayingPart(part, fadedFramesCount)) break; mCallbacks->playPart(i, part, r); @@ -1153,7 +1214,9 @@ bool BootAnimation::playAnimation(const Animation& animation) { part.backgroundColor[2], 1.0f); - for (size_t j=0 ; j<fcount && (!exitPending() || part.playUntilComplete) ; j++) { + for (size_t j=0 ; j<fcount ; j++) { + if (shouldStopPlayingPart(part, fadedFramesCount)) break; + processDisplayEvents(); const int animationX = (mWidth - animation.width) / 2; @@ -1192,11 +1255,22 @@ bool BootAnimation::playAnimation(const Animation& animation) { } // specify the y center as ceiling((mHeight - frame.trimHeight) / 2) // which is equivalent to mHeight - (yc + frame.trimHeight) - glDrawTexiOES(xc, mHeight - (yc + frame.trimHeight), - 0, frame.trimWidth, frame.trimHeight); + const int frameDrawY = mHeight - (yc + frame.trimHeight); + glDrawTexiOES(xc, frameDrawY, 0, frame.trimWidth, frame.trimHeight); + + // if the part hasn't been stopped yet then continue fading if necessary + if (exitPending() && part.hasFadingPhase()) { + fadeFrame(xc, frameDrawY, frame.trimWidth, frame.trimHeight, part, + ++fadedFramesCount); + if (fadedFramesCount >= part.framesToFadeCount) { + fadedFramesCount = MAX_FADED_FRAMES_COUNT; // no more fading + } + } + if (mClockEnabled && mTimeIsAccurate && validClock(part)) { drawClock(animation.clockFont, part.clockPosX, part.clockPosY); } + handleViewport(frameDuration); eglSwapBuffers(mDisplay, mSurface); @@ -1221,11 +1295,11 @@ bool BootAnimation::playAnimation(const Animation& animation) { usleep(part.pause * ns2us(frameDuration)); - // For infinite parts, we've now played them at least once, so perhaps exit - if(exitPending() && !part.count && mCurrentInset >= mTargetInset) - break; + if (exitPending() && !part.count && mCurrentInset >= mTargetInset && + !part.hasFadingPhase()) { + break; // exit the infinite non-fading part when it has been played at least once + } } - } // Free textures created for looping parts now that the animation is done. diff --git a/cmds/bootanimation/BootAnimation.h b/cmds/bootanimation/BootAnimation.h index 9e6e4aa42f1c..4699cfe2ac2d 100644 --- a/cmds/bootanimation/BootAnimation.h +++ b/cmds/bootanimation/BootAnimation.h @@ -19,6 +19,7 @@ #include <vector> #include <queue> +#include <climits> #include <stdint.h> #include <sys/types.h> @@ -43,6 +44,8 @@ class SurfaceControl; class BootAnimation : public Thread, public IBinder::DeathRecipient { public: + static constexpr int MAX_FADED_FRAMES_COUNT = std::numeric_limits<int>::max(); + struct Texture { GLint w; GLint h; @@ -82,10 +85,15 @@ public: String8 trimData; SortedVector<Frame> frames; bool playUntilComplete; + int framesToFadeCount; float backgroundColor[3]; uint8_t* audioData; int audioLength; Animation* animation; + + bool hasFadingPhase() const { + return !playUntilComplete && framesToFadeCount > 0; + } }; int fps; int width; @@ -160,6 +168,8 @@ private: bool movie(); void drawText(const char* str, const Font& font, bool bold, int* x, int* y); void drawClock(const Font& font, const int xPos, const int yPos); + void fadeFrame(int frameLeft, int frameBottom, int frameWidth, int frameHeight, + const Animation::Part& part, int fadedFramesCount); bool validClock(const Animation::Part& part); Animation* loadAnimation(const String8&); bool playAnimation(const Animation&); @@ -172,7 +182,9 @@ private: EGLConfig getEglConfig(const EGLDisplay&); ui::Size limitSurfaceSize(int width, int height) const; void resizeSurface(int newWidth, int newHeight); + void projectSceneToWindow(); + bool shouldStopPlayingPart(const Animation::Part& part, int fadedFramesCount); void checkExit(); void handleViewport(nsecs_t timestep); diff --git a/cmds/bootanimation/FORMAT.md b/cmds/bootanimation/FORMAT.md index 5946515aa263..f9b83c957d5b 100644 --- a/cmds/bootanimation/FORMAT.md +++ b/cmds/bootanimation/FORMAT.md @@ -30,14 +30,20 @@ The first line defines the general parameters of the animation: It is followed by a number of rows of the form: - TYPE COUNT PAUSE PATH [#RGBHEX [CLOCK1 [CLOCK2]]] + TYPE COUNT PAUSE PATH [FADE [#RGBHEX [CLOCK1 [CLOCK2]]]] * **TYPE:** a single char indicating what type of animation segment this is: + `p` -- this part will play unless interrupted by the end of the boot + `c` -- this part will play to completion, no matter what + + `f` -- same as `p` but in addition the specified number of frames is being faded out while + continue playing. Only the first interrupted `f` part is faded out, other subsequent `f` + parts are skipped * **COUNT:** how many times to play the animation, or 0 to loop forever until boot is complete * **PAUSE:** number of FRAMES to delay after this part ends * **PATH:** directory in which to find the frames for this part (e.g. `part0`) + * **FADE:** _(ONLY FOR `f` TYPE)_ number of frames to fade out when interrupted where `0` means + _immediately_ which makes `f ... 0` behave like `p` and doesn't count it as a fading + part * **RGBHEX:** _(OPTIONAL)_ a background color, specified as `#RRGGBB` * **CLOCK1, CLOCK2:** _(OPTIONAL)_ the coordinates at which to draw the current time (for watches): + If only `CLOCK1` is provided it is the y-coordinate of the clock and the x-coordinate diff --git a/cmds/statsd/src/condition/ConditionTimer.h b/cmds/statsd/src/condition/ConditionTimer.h index 442bc11934fe..1fbe25279736 100644 --- a/cmds/statsd/src/condition/ConditionTimer.h +++ b/cmds/statsd/src/condition/ConditionTimer.h @@ -36,7 +36,7 @@ class ConditionTimer { public: explicit ConditionTimer(bool initCondition, int64_t bucketStartNs) : mCondition(initCondition) { if (initCondition) { - mLastConditionTrueTimestampNs = bucketStartNs; + mLastConditionChangeTimestampNs = bucketStartNs; } }; @@ -44,21 +44,46 @@ public: // When a new bucket is created, this value will be reset to 0. int64_t mTimerNs = 0; - // Last elapsed real timestamp when condition turned to true - // When a new bucket is created and the condition is true, then the timestamp is set - // to be the bucket start timestamp. - int64_t mLastConditionTrueTimestampNs = 0; + // Last elapsed real timestamp when condition changed. + int64_t mLastConditionChangeTimestampNs = 0; bool mCondition = false; int64_t newBucketStart(int64_t nextBucketStartNs) { if (mCondition) { - mTimerNs += (nextBucketStartNs - mLastConditionTrueTimestampNs); - mLastConditionTrueTimestampNs = nextBucketStartNs; + // Normally, the next bucket happens after the last condition + // change. In this case, add the time between the condition becoming + // true to the next bucket start time. + // Otherwise, the next bucket start time is before the last + // condition change time, this means that the condition was false at + // the bucket boundary before the condition became true, so the + // timer should not get updated and the last condition change time + // remains as is. + if (nextBucketStartNs >= mLastConditionChangeTimestampNs) { + mTimerNs += (nextBucketStartNs - mLastConditionChangeTimestampNs); + mLastConditionChangeTimestampNs = nextBucketStartNs; + } + } else if (mLastConditionChangeTimestampNs > nextBucketStartNs) { + // The next bucket start time is before the last condition change + // time, this means that the condition was true at the bucket + // boundary before the condition became false, so adjust the timer + // to match how long the condition was true to the bucket boundary. + // This means remove the amount the condition stayed true in the + // next bucket from the current bucket. + mTimerNs -= (mLastConditionChangeTimestampNs - nextBucketStartNs); } int64_t temp = mTimerNs; mTimerNs = 0; + + if (!mCondition && (mLastConditionChangeTimestampNs > nextBucketStartNs)) { + // The next bucket start time is before the last condition change + // time, this means that the condition was true at the bucket + // boundary and remained true in the next bucket up to the condition + // change to false, so adjust the timer to match how long the + // condition stayed true in the next bucket (now the current bucket). + mTimerNs = mLastConditionChangeTimestampNs - nextBucketStartNs; + } return temp; } @@ -67,11 +92,10 @@ public: return; } mCondition = newCondition; - if (newCondition) { - mLastConditionTrueTimestampNs = timestampNs; - } else { - mTimerNs += (timestampNs - mLastConditionTrueTimestampNs); + if (newCondition == false) { + mTimerNs += (timestampNs - mLastConditionChangeTimestampNs); } + mLastConditionChangeTimestampNs = timestampNs; } FRIEND_TEST(ConditionTimerTest, TestTimer_Inital_False); @@ -80,4 +104,4 @@ public: } // namespace statsd } // namespace os -} // namespace android
\ No newline at end of file +} // namespace android diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp index 39ae9a47f2bf..3d57cfe318c5 100644 --- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp +++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp @@ -301,7 +301,6 @@ void ValueMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs, protoOutput->write(FIELD_TYPE_INT32 | FIELD_ID_BUCKET_DROP_REASON, dropEvent.reason); protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_DROP_TIME, (long long)(NanoToMillis(dropEvent.dropTimeNs))); - ; protoOutput->end(dropEventToken); } protoOutput->end(wrapperToken); @@ -346,8 +345,11 @@ void ValueMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs, protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_BUCKET_NUM, (long long)(getBucketNumFromEndTimeNs(bucket.mBucketEndNs))); } - // only write the condition timer value if the metric has a condition. - if (mConditionTrackerIndex >= 0) { + // We only write the condition timer value if the metric has a + // condition and/or is sliced by state. + // If the metric is sliced by state, the condition timer value is + // also sliced by state to reflect time spent in that state. + if (mConditionTrackerIndex >= 0 || !mSlicedStateAtoms.empty()) { protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_CONDITION_TRUE_NS, (long long)bucket.mConditionTrueNs); } @@ -454,6 +456,8 @@ void ValueMetricProducer::onActiveStateChangedLocked(const int64_t& eventTimeNs) // Let condition timer know of new active state. mConditionTimer.onConditionChanged(mIsActive, eventTimeNs); + + updateCurrentSlicedBucketConditionTimers(mIsActive, eventTimeNs); } void ValueMetricProducer::onConditionChangedLocked(const bool condition, @@ -476,6 +480,8 @@ void ValueMetricProducer::onConditionChangedLocked(const bool condition, invalidateCurrentBucket(eventTimeNs, BucketDropReason::EVENT_IN_WRONG_BUCKET); mCondition = ConditionState::kUnknown; mConditionTimer.onConditionChanged(mCondition, eventTimeNs); + + updateCurrentSlicedBucketConditionTimers(mCondition, eventTimeNs); return; } @@ -517,6 +523,29 @@ void ValueMetricProducer::onConditionChangedLocked(const bool condition, flushIfNeededLocked(eventTimeNs); mConditionTimer.onConditionChanged(mCondition, eventTimeNs); + + updateCurrentSlicedBucketConditionTimers(mCondition, eventTimeNs); +} + +void ValueMetricProducer::updateCurrentSlicedBucketConditionTimers(bool newCondition, + int64_t eventTimeNs) { + if (mSlicedStateAtoms.empty()) { + return; + } + + // Utilize the current state key of each DimensionsInWhat key to determine + // which condition timers to update. + // + // Assumes that the MetricDimensionKey exists in `mCurrentSlicedBucket`. + bool inPulledData; + for (const auto& [dimensionInWhatKey, dimensionInWhatInfo] : mCurrentBaseInfo) { + // If the new condition is true, turn ON the condition timer only if + // the DimensionInWhat key was present in the pulled data. + inPulledData = dimensionInWhatInfo.hasCurrentState; + mCurrentSlicedBucket[MetricDimensionKey(dimensionInWhatKey, + dimensionInWhatInfo.currentState)] + .conditionTimer.onConditionChanged(newCondition && inPulledData, eventTimeNs); + } } void ValueMetricProducer::prepareFirstBucketLocked() { @@ -618,8 +647,8 @@ void ValueMetricProducer::accumulateEvents(const std::vector<std::shared_ptr<Log // 2. A superset of the current mStateChangePrimaryKey // was not found in the new pulled data (i.e. not in mMatchedDimensionInWhatKeys) // then we need to reset the base. - for (auto& slice : mCurrentSlicedBucket) { - const auto& whatKey = slice.first.getDimensionKeyInWhat(); + for (auto& [metricDimensionKey, currentValueBucket] : mCurrentSlicedBucket) { + const auto& whatKey = metricDimensionKey.getDimensionKeyInWhat(); bool presentInPulledData = mMatchedMetricDimensionKeys.find(whatKey) != mMatchedMetricDimensionKeys.end(); if (!presentInPulledData && whatKey.contains(mStateChangePrimaryKey.second)) { @@ -627,6 +656,12 @@ void ValueMetricProducer::accumulateEvents(const std::vector<std::shared_ptr<Log for (auto& baseInfo : it->second.baseInfos) { baseInfo.hasBase = false; } + // Set to false when DimensionInWhat key is not present in a pull. + // Used in onMatchedLogEventInternalLocked() to ensure the condition + // timer is turned on the next pull when data is present. + it->second.hasCurrentState = false; + // Turn OFF condition timer for keys not present in pulled data. + currentValueBucket.conditionTimer.onConditionChanged(false, eventElapsedTimeNs); } } mMatchedMetricDimensionKeys.clear(); @@ -789,21 +824,26 @@ void ValueMetricProducer::onMatchedLogEventInternalLocked( return; } - DimensionsInWhatInfo& dimensionsInWhatInfo = mCurrentBaseInfo[whatKey]; + const auto& returnVal = + mCurrentBaseInfo.emplace(whatKey, DimensionsInWhatInfo(getUnknownStateKey())); + DimensionsInWhatInfo& dimensionsInWhatInfo = returnVal.first->second; + const HashableDimensionKey oldStateKey = dimensionsInWhatInfo.currentState; vector<BaseInfo>& baseInfos = dimensionsInWhatInfo.baseInfos; if (baseInfos.size() < mFieldMatchers.size()) { VLOG("Resizing number of intervals to %d", (int)mFieldMatchers.size()); baseInfos.resize(mFieldMatchers.size()); } + // Ensure we turn on the condition timer in the case where dimensions + // were missing on a previous pull due to a state change. + bool stateChange = oldStateKey != stateKey; if (!dimensionsInWhatInfo.hasCurrentState) { - dimensionsInWhatInfo.currentState = getUnknownStateKey(); + stateChange = true; dimensionsInWhatInfo.hasCurrentState = true; } // We need to get the intervals stored with the previous state key so we can // close these value intervals. - const auto oldStateKey = dimensionsInWhatInfo.currentState; vector<Interval>& intervals = mCurrentSlicedBucket[MetricDimensionKey(whatKey, oldStateKey)].intervals; if (intervals.size() < mFieldMatchers.size()) { @@ -916,6 +956,17 @@ void ValueMetricProducer::onMatchedLogEventInternalLocked( interval.sampleSize += 1; } + // State change. + if (!mSlicedStateAtoms.empty() && stateChange) { + // Turn OFF the condition timer for the previous state key. + mCurrentSlicedBucket[MetricDimensionKey(whatKey, oldStateKey)] + .conditionTimer.onConditionChanged(false, eventTimeNs); + + // Turn ON the condition timer for the new state key. + mCurrentSlicedBucket[MetricDimensionKey(whatKey, stateKey)] + .conditionTimer.onConditionChanged(true, eventTimeNs); + } + // Only trigger the tracker if all intervals are correct and we have not skipped the bucket due // to MULTIPLE_BUCKETS_SKIPPED. if (useAnomalyDetection && !multipleBucketsSkipped(calcBucketsForwardCount(eventTimeNs))) { @@ -990,12 +1041,18 @@ void ValueMetricProducer::flushCurrentBucketLocked(const int64_t& eventTimeNs, if (!mCurrentBucketIsSkipped) { bool bucketHasData = false; // The current bucket is large enough to keep. - for (const auto& slice : mCurrentSlicedBucket) { - PastValueBucket bucket = buildPartialBucket(bucketEndTime, slice.second.intervals); - bucket.mConditionTrueNs = conditionTrueDuration; + for (auto& [metricDimensionKey, currentValueBucket] : mCurrentSlicedBucket) { + PastValueBucket bucket = + buildPartialBucket(bucketEndTime, currentValueBucket.intervals); + if (!mSlicedStateAtoms.empty()) { + bucket.mConditionTrueNs = + currentValueBucket.conditionTimer.newBucketStart(bucketEndTime); + } else { + bucket.mConditionTrueNs = conditionTrueDuration; + } // it will auto create new vector of ValuebucketInfo if the key is not found. if (bucket.valueIndex.size() > 0) { - auto& bucketList = mPastBuckets[slice.first]; + auto& bucketList = mPastBuckets[metricDimensionKey]; bucketList.push_back(bucket); bucketHasData = true; } @@ -1023,11 +1080,18 @@ void ValueMetricProducer::flushCurrentBucketLocked(const int64_t& eventTimeNs, buildDropEvent(eventTimeNs, BucketDropReason::NO_DATA)); mSkippedBuckets.emplace_back(bucketInGap); } - appendToFullBucket(eventTimeNs > fullBucketEndTimeNs); initCurrentSlicedBucket(nextBucketStartTimeNs); // Update the condition timer again, in case we skipped buckets. mConditionTimer.newBucketStart(nextBucketStartTimeNs); + + // NOTE: Update the condition timers in `mCurrentSlicedBucket` only when slicing + // by state. Otherwise, the "global" condition timer will be used. + if (!mSlicedStateAtoms.empty()) { + for (auto& [metricDimensionKey, currentValueBucket] : mCurrentSlicedBucket) { + currentValueBucket.conditionTimer.newBucketStart(nextBucketStartTimeNs); + } + } mCurrentBucketNum += numBucketsForward; } @@ -1069,6 +1133,17 @@ void ValueMetricProducer::initCurrentSlicedBucket(int64_t nextBucketStartTimeNs) interval.seenNewData = false; } + if (obsolete && !mSlicedStateAtoms.empty()) { + // When slicing by state, only delete the MetricDimensionKey when the + // state key in the MetricDimensionKey is not the current state key. + const HashableDimensionKey& dimensionInWhatKey = it->first.getDimensionKeyInWhat(); + const auto& currentBaseInfoItr = mCurrentBaseInfo.find(dimensionInWhatKey); + + if ((currentBaseInfoItr != mCurrentBaseInfo.end()) && + (it->first.getStateValuesKey() == currentBaseInfoItr->second.currentState)) { + obsolete = false; + } + } if (obsolete) { it = mCurrentSlicedBucket.erase(it); } else { @@ -1104,7 +1179,7 @@ void ValueMetricProducer::appendToFullBucket(const bool isFullBucketReached) { // Accumulate partial buckets with current value and then send to anomaly tracker. if (mCurrentFullBucket.size() > 0) { for (const auto& slice : mCurrentSlicedBucket) { - if (hitFullBucketGuardRailLocked(slice.first)) { + if (hitFullBucketGuardRailLocked(slice.first) || slice.second.intervals.empty()) { continue; } // TODO: fix this when anomaly can accept double values @@ -1125,7 +1200,7 @@ void ValueMetricProducer::appendToFullBucket(const bool isFullBucketReached) { // Skip aggregating the partial buckets since there's no previous partial bucket. for (const auto& slice : mCurrentSlicedBucket) { for (auto& tracker : mAnomalyTrackers) { - if (tracker != nullptr) { + if (tracker != nullptr && !slice.second.intervals.empty()) { // TODO: fix this when anomaly can accept double values auto& interval = slice.second.intervals[0]; if (interval.hasValue) { @@ -1139,10 +1214,12 @@ void ValueMetricProducer::appendToFullBucket(const bool isFullBucketReached) { } else { // Accumulate partial bucket. for (const auto& slice : mCurrentSlicedBucket) { - // TODO: fix this when anomaly can accept double values - auto& interval = slice.second.intervals[0]; - if (interval.hasValue) { - mCurrentFullBucket[slice.first] += interval.value.long_value; + if (!slice.second.intervals.empty()) { + // TODO: fix this when anomaly can accept double values + auto& interval = slice.second.intervals[0]; + if (interval.hasValue) { + mCurrentFullBucket[slice.first] += interval.value.long_value; + } } } } diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h index 4b2599bdb517..67de214e655c 100644 --- a/cmds/statsd/src/metrics/ValueMetricProducer.h +++ b/cmds/statsd/src/metrics/ValueMetricProducer.h @@ -193,8 +193,14 @@ private: // Internal state of an ongoing aggregation bucket. typedef struct CurrentValueBucket { + // If the `MetricDimensionKey` state key is the current state key, then + // the condition timer will be updated later (e.g. condition/state/active + // state change) with the correct condition and time. + CurrentValueBucket() : intervals(), conditionTimer(ConditionTimer(false, 0)) {} // Value information for each value field of the metric. std::vector<Interval> intervals; + // Tracks how long the condition is true. + ConditionTimer conditionTimer; } CurrentValueBucket; // Holds base information for diffing values from one value field. @@ -206,7 +212,10 @@ private: } BaseInfo; // State key and base information for a specific DimensionsInWhat key. - typedef struct { + typedef struct DimensionsInWhatInfo { + DimensionsInWhatInfo(const HashableDimensionKey& stateKey) + : baseInfos(), currentState(stateKey), hasCurrentState(false) { + } std::vector<BaseInfo> baseInfos; // Last seen state value(s). HashableDimensionKey currentState; @@ -252,6 +261,10 @@ private: // Reset diff base and mHasGlobalBase void resetBase(); + // Updates the condition timers in the current sliced bucket when there is a + // condition change or an active state change. + void updateCurrentSlicedBucketConditionTimers(bool newCondition, int64_t eventTimeNs); + static const size_t kBucketSize = sizeof(PastValueBucket{}); const size_t mDimensionSoftLimit; @@ -337,6 +350,11 @@ private: FRIEND_TEST(ValueMetricProducerTest, TestTrimUnusedDimensionKey); FRIEND_TEST(ValueMetricProducerTest, TestUseZeroDefaultBase); FRIEND_TEST(ValueMetricProducerTest, TestUseZeroDefaultBaseWithPullFailures); + FRIEND_TEST(ValueMetricProducerTest, TestSlicedStateWithMultipleDimensions); + FRIEND_TEST(ValueMetricProducerTest, TestSlicedStateWithMissingDataInStateChange); + FRIEND_TEST(ValueMetricProducerTest, TestSlicedStateWithDataMissingInConditionChange); + FRIEND_TEST(ValueMetricProducerTest, TestSlicedStateWithMissingDataThenFlushBucket); + FRIEND_TEST(ValueMetricProducerTest, TestSlicedStateWithNoPullOnBucketBoundary); FRIEND_TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenOneConditionFailed); FRIEND_TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenInitialPullFailed); diff --git a/cmds/statsd/src/metrics/parsing_utils/config_update_utils.cpp b/cmds/statsd/src/metrics/parsing_utils/config_update_utils.cpp index 5dbf16deb552..2ae57638791e 100644 --- a/cmds/statsd/src/metrics/parsing_utils/config_update_utils.cpp +++ b/cmds/statsd/src/metrics/parsing_utils/config_update_utils.cpp @@ -24,6 +24,8 @@ #include "matchers/EventMatcherWizard.h" #include "metrics_manager_util.h" +using google::protobuf::MessageLite; + namespace android { namespace os { namespace statsd { @@ -419,16 +421,19 @@ bool metricActivationDepsChange(const StatsdConfig& config, return false; } -bool determineEventMetricUpdateStatus(const StatsdConfig& config, const EventMetric& metric, - const unordered_map<int64_t, int>& oldMetricProducerMap, - const vector<sp<MetricProducer>>& oldMetricProducers, - const unordered_map<int64_t, int>& metricToActivationMap, - const set<int64_t>& replacedMatchers, - const set<int64_t>& replacedConditions, - UpdateStatus& updateStatus) { - int64_t id = metric.id(); +bool determineMetricUpdateStatus( + const StatsdConfig& config, const MessageLite& metric, const int64_t metricId, + const MetricType metricType, const set<int64_t>& matcherDependencies, + const set<int64_t>& conditionDependencies, + const ::google::protobuf::RepeatedField<int64_t>& stateDependencies, + const ::google::protobuf::RepeatedPtrField<MetricConditionLink>& conditionLinks, + const unordered_map<int64_t, int>& oldMetricProducerMap, + const vector<sp<MetricProducer>>& oldMetricProducers, + const unordered_map<int64_t, int>& metricToActivationMap, + const set<int64_t>& replacedMatchers, const set<int64_t>& replacedConditions, + const set<int64_t>& replacedStates, UpdateStatus& updateStatus) { // Check if new metric - const auto& oldMetricProducerIt = oldMetricProducerMap.find(id); + const auto& oldMetricProducerIt = oldMetricProducerMap.find(metricId); if (oldMetricProducerIt == oldMetricProducerMap.end()) { updateStatus = UPDATE_NEW; return true; @@ -436,41 +441,82 @@ bool determineEventMetricUpdateStatus(const StatsdConfig& config, const EventMet // This is an existing metric, check if it has changed. uint64_t metricHash; - if (!getMetricProtoHash(config, metric, id, metricToActivationMap, metricHash)) { + if (!getMetricProtoHash(config, metric, metricId, metricToActivationMap, metricHash)) { return false; } const sp<MetricProducer> oldMetricProducer = oldMetricProducers[oldMetricProducerIt->second]; - if (oldMetricProducer->getMetricType() != METRIC_TYPE_EVENT || + if (oldMetricProducer->getMetricType() != metricType || oldMetricProducer->getProtoHash() != metricHash) { updateStatus = UPDATE_REPLACE; return true; } - // Metric type and definition are the same. Need to check dependencies to see if they changed. - if (replacedMatchers.find(metric.what()) != replacedMatchers.end()) { + // Take intersections of the matchers/predicates/states that the metric + // depends on with those that have been replaced. If a metric depends on any + // replaced component, it too must be replaced. + set<int64_t> intersection; + set_intersection(matcherDependencies.begin(), matcherDependencies.end(), + replacedMatchers.begin(), replacedMatchers.end(), + inserter(intersection, intersection.begin())); + if (intersection.size() > 0) { + updateStatus = UPDATE_REPLACE; + return true; + } + set_intersection(conditionDependencies.begin(), conditionDependencies.end(), + replacedConditions.begin(), replacedConditions.end(), + inserter(intersection, intersection.begin())); + if (intersection.size() > 0) { + updateStatus = UPDATE_REPLACE; + return true; + } + set_intersection(stateDependencies.begin(), stateDependencies.end(), replacedStates.begin(), + replacedStates.end(), inserter(intersection, intersection.begin())); + if (intersection.size() > 0) { updateStatus = UPDATE_REPLACE; return true; } - if (metric.has_condition()) { - if (replacedConditions.find(metric.condition()) != replacedConditions.end()) { + for (const auto& metricConditionLink : conditionLinks) { + if (replacedConditions.find(metricConditionLink.condition()) != replacedConditions.end()) { updateStatus = UPDATE_REPLACE; return true; } } - if (metricActivationDepsChange(config, metricToActivationMap, id, replacedMatchers)) { + if (metricActivationDepsChange(config, metricToActivationMap, metricId, replacedMatchers)) { updateStatus = UPDATE_REPLACE; return true; } - for (const auto& metricConditionLink : metric.links()) { - if (replacedConditions.find(metricConditionLink.condition()) != replacedConditions.end()) { - updateStatus = UPDATE_REPLACE; - return true; + updateStatus = UPDATE_PRESERVE; + return true; +} + +bool determineAllMetricUpdateStatuses(const StatsdConfig& config, + const unordered_map<int64_t, int>& oldMetricProducerMap, + const vector<sp<MetricProducer>>& oldMetricProducers, + const unordered_map<int64_t, int>& metricToActivationMap, + const set<int64_t>& replacedMatchers, + const set<int64_t>& replacedConditions, + const set<int64_t>& replacedStates, + vector<UpdateStatus>& metricsToUpdate) { + int metricIndex = 0; + for (int i = 0; i < config.event_metric_size(); i++, metricIndex++) { + const EventMetric& metric = config.event_metric(i); + set<int64_t> conditionDependencies; + if (metric.has_condition()) { + conditionDependencies.insert(metric.condition()); + } + if (!determineMetricUpdateStatus( + config, metric, metric.id(), METRIC_TYPE_EVENT, {metric.what()}, + conditionDependencies, ::google::protobuf::RepeatedField<int64_t>(), + metric.links(), oldMetricProducerMap, oldMetricProducers, metricToActivationMap, + replacedMatchers, replacedConditions, replacedStates, + metricsToUpdate[metricIndex])) { + return false; } } - updateStatus = UPDATE_PRESERVE; + // TODO: determine update status for count, gauge, value, duration metrics. return true; } @@ -518,22 +564,16 @@ bool updateMetrics(const ConfigKey& key, const StatsdConfig& config, const int64 } vector<UpdateStatus> metricsToUpdate(allMetricsCount, UPDATE_UNKNOWN); - int metricIndex = 0; - for (int i = 0; i < config.event_metric_size(); i++, metricIndex++) { - newMetricProducerMap[config.event_metric(i).id()] = metricIndex; - if (!determineEventMetricUpdateStatus(config, config.event_metric(i), oldMetricProducerMap, - oldMetricProducers, metricToActivationMap, - replacedMatchers, replacedConditions, - metricsToUpdate[metricIndex])) { - return false; - } + if (!determineAllMetricUpdateStatuses(config, oldMetricProducerMap, oldMetricProducers, + metricToActivationMap, replacedMatchers, + replacedConditions, replacedStates, metricsToUpdate)) { + return false; } - // TODO: determine update status for count, gauge, value, duration metrics. - // Now, perform the update. Must iterate the metric types in the same order - metricIndex = 0; + int metricIndex = 0; for (int i = 0; i < config.event_metric_size(); i++, metricIndex++) { + newMetricProducerMap[config.event_metric(i).id()] = metricIndex; const EventMetric& metric = config.event_metric(i); switch (metricsToUpdate[metricIndex]) { case UPDATE_PRESERVE: { diff --git a/cmds/statsd/src/metrics/parsing_utils/config_update_utils.h b/cmds/statsd/src/metrics/parsing_utils/config_update_utils.h index 1cd0ce524b31..34d7e9c7de9e 100644 --- a/cmds/statsd/src/metrics/parsing_utils/config_update_utils.h +++ b/cmds/statsd/src/metrics/parsing_utils/config_update_utils.h @@ -125,23 +125,25 @@ bool updateConditions(const ConfigKey& key, const StatsdConfig& config, std::vector<ConditionState>& conditionCache, std::set<int64_t>& replacedConditions); -// Function to determine if an event metric needs to be updated. Populates updateStatus. +// Function to determine the update status (preserve/replace/new) of all metrics in the config. // [config]: the input StatsdConfig -// [metric]: the current metric to be updated // [oldMetricProducerMap]: metric id to index mapping in the existing MetricsManager // [oldMetricProducers]: stores the existing MetricProducers -// [metricToActivationMap]: map from metric id to metric activation index. -// [replacedMatchers]: set of replaced matcher ids. conditions using these matchers must be replaced +// [metricToActivationMap]: map from metric id to metric activation index +// [replacedMatchers]: set of replaced matcher ids. metrics using these matchers must be replaced +// [replacedConditions]: set of replaced conditions. metrics using these conditions must be replaced +// [replacedStates]: set of replaced state ids. metrics using these states must be replaced // output: -// [updateStatus]: update status for the metric. Will be changed from UPDATE_UNKNOWN after this call +// [metricsToUpdate]: update status of each metric. Will be changed from UPDATE_UNKNOWN // Returns whether the function was successful or not. -bool determineEventMetricUpdateStatus(const StatsdConfig& config, const EventMetric& metric, +bool determineAllMetricUpdateStatuses(const StatsdConfig& config, const unordered_map<int64_t, int>& oldMetricProducerMap, const vector<sp<MetricProducer>>& oldMetricProducers, const unordered_map<int64_t, int>& metricToActivationMap, const set<int64_t>& replacedMatchers, const set<int64_t>& replacedConditions, - UpdateStatus& updateStatus); + const set<int64_t>& replacedStates, + vector<UpdateStatus>& metricsToUpdate); // Update MetricProducers. // input: diff --git a/cmds/statsd/tests/condition/ConditionTimer_test.cpp b/cmds/statsd/tests/condition/ConditionTimer_test.cpp index ea02cd3a5ee1..46dc9a9d381f 100644 --- a/cmds/statsd/tests/condition/ConditionTimer_test.cpp +++ b/cmds/statsd/tests/condition/ConditionTimer_test.cpp @@ -35,11 +35,11 @@ TEST(ConditionTimerTest, TestTimer_Inital_False) { EXPECT_EQ(0, timer.mTimerNs); timer.onConditionChanged(true, ct_start_time + 5); - EXPECT_EQ(ct_start_time + 5, timer.mLastConditionTrueTimestampNs); + EXPECT_EQ(ct_start_time + 5, timer.mLastConditionChangeTimestampNs); EXPECT_EQ(true, timer.mCondition); EXPECT_EQ(95, timer.newBucketStart(ct_start_time + 100)); - EXPECT_EQ(ct_start_time + 100, timer.mLastConditionTrueTimestampNs); + EXPECT_EQ(ct_start_time + 100, timer.mLastConditionChangeTimestampNs); EXPECT_EQ(true, timer.mCondition); } @@ -51,7 +51,7 @@ TEST(ConditionTimerTest, TestTimer_Inital_True) { EXPECT_EQ(ct_start_time - time_base, timer.newBucketStart(ct_start_time)); EXPECT_EQ(true, timer.mCondition); EXPECT_EQ(0, timer.mTimerNs); - EXPECT_EQ(ct_start_time, timer.mLastConditionTrueTimestampNs); + EXPECT_EQ(ct_start_time, timer.mLastConditionChangeTimestampNs); timer.onConditionChanged(false, ct_start_time + 5); EXPECT_EQ(5, timer.mTimerNs); diff --git a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp index b166cc1fe04e..6cf4192b41fd 100644 --- a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp +++ b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp @@ -93,6 +93,13 @@ static void assertPastBucketValuesSingleKey( } } +static void assertConditionTimer(const ConditionTimer& conditionTimer, bool condition, + int64_t timerNs, int64_t lastConditionTrueTimestampNs) { + EXPECT_EQ(condition, conditionTimer.mCondition); + EXPECT_EQ(timerNs, conditionTimer.mTimerNs); + EXPECT_EQ(lastConditionTrueTimestampNs, conditionTimer.mLastConditionChangeTimestampNs); +} + } // anonymous namespace class ValueMetricProducerTestHelper { @@ -3967,33 +3974,37 @@ TEST(ValueMetricProducerTest, TestSlicedState) { // Screen state change to ON. .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, vector<std::shared_ptr<LogEvent>>* data) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 5); + EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 5 * NS_PER_SEC); data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 5, 5)); + data->push_back( + CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 5 * NS_PER_SEC, 5)); return true; })) // Screen state change to OFF. .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, vector<std::shared_ptr<LogEvent>>* data) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10); + EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10 * NS_PER_SEC); data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 10, 9)); + data->push_back( + CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 10 * NS_PER_SEC, 9)); return true; })) // Screen state change to ON. .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, vector<std::shared_ptr<LogEvent>>* data) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 15); + EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 15 * NS_PER_SEC); data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 15, 21)); + data->push_back(CreateRepeatedValueLogEvent( + tagId, bucketStartTimeNs + 15 * NS_PER_SEC, 21)); return true; })) // Dump report requested. .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, vector<std::shared_ptr<LogEvent>>* data) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 50); + EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 50 * NS_PER_SEC); data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 50, 30)); + data->push_back(CreateRepeatedValueLogEvent( + tagId, bucketStartTimeNs + 50 * NS_PER_SEC, 30)); return true; })); @@ -4025,12 +4036,13 @@ TEST(ValueMetricProducerTest, TestSlicedState) { EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, it->first.getStateValuesKey().getValues()[0].mValue.int_value); EXPECT_FALSE(it->second.intervals[0].hasValue); + assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs); // Bucket status after screen state change kStateUnknown->ON. auto screenEvent = CreateScreenStateChangedEvent( - bucketStartTimeNs + 5, android::view::DisplayStateEnum::DISPLAY_STATE_ON); + bucketStartTimeNs + 5 * NS_PER_SEC, android::view::DisplayStateEnum::DISPLAY_STATE_ON); StateManager::getInstance().onLogEvent(*screenEvent); - ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); + ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size()); // Base for dimension key {} it = valueProducer->mCurrentSlicedBucket.begin(); itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); @@ -4040,19 +4052,29 @@ TEST(ValueMetricProducerTest, TestSlicedState) { ASSERT_EQ(1, itBase->second.currentState.getValues().size()); EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON, itBase->second.currentState.getValues()[0].mValue.int_value); + // Value for dimension, state key {{}, ON} + EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); + ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); + EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON, + it->first.getStateValuesKey().getValues()[0].mValue.int_value); + EXPECT_EQ(0, it->second.intervals.size()); + assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 5 * NS_PER_SEC); // Value for dimension, state key {{}, kStateUnknown} + it++; EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, it->first.getStateValuesKey().getValues()[0].mValue.int_value); EXPECT_TRUE(it->second.intervals[0].hasValue); EXPECT_EQ(2, it->second.intervals[0].value.long_value); + assertConditionTimer(it->second.conditionTimer, false, 5 * NS_PER_SEC, + bucketStartTimeNs + 5 * NS_PER_SEC); // Bucket status after screen state change ON->OFF. - screenEvent = CreateScreenStateChangedEvent(bucketStartTimeNs + 10, + screenEvent = CreateScreenStateChangedEvent(bucketStartTimeNs + 10 * NS_PER_SEC, android::view::DisplayStateEnum::DISPLAY_STATE_OFF); StateManager::getInstance().onLogEvent(*screenEvent); - ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size()); + ASSERT_EQ(3UL, valueProducer->mCurrentSlicedBucket.size()); // Base for dimension key {} it = valueProducer->mCurrentSlicedBucket.begin(); itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); @@ -4061,13 +4083,23 @@ TEST(ValueMetricProducerTest, TestSlicedState) { EXPECT_TRUE(itBase->second.hasCurrentState); EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_OFF, itBase->second.currentState.getValues()[0].mValue.int_value); + // Value for dimension, state key {{}, OFF} + EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); + ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); + EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_OFF, + it->first.getStateValuesKey().getValues()[0].mValue.int_value); + EXPECT_EQ(0, it->second.intervals.size()); + assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 10 * NS_PER_SEC); // Value for dimension, state key {{}, ON} + it++; EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON, it->first.getStateValuesKey().getValues()[0].mValue.int_value); EXPECT_TRUE(it->second.intervals[0].hasValue); EXPECT_EQ(4, it->second.intervals[0].value.long_value); + assertConditionTimer(it->second.conditionTimer, false, 5 * NS_PER_SEC, + bucketStartTimeNs + 10 * NS_PER_SEC); // Value for dimension, state key {{}, kStateUnknown} it++; EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); @@ -4076,9 +4108,11 @@ TEST(ValueMetricProducerTest, TestSlicedState) { it->first.getStateValuesKey().getValues()[0].mValue.int_value); EXPECT_TRUE(it->second.intervals[0].hasValue); EXPECT_EQ(2, it->second.intervals[0].value.long_value); + assertConditionTimer(it->second.conditionTimer, false, 5 * NS_PER_SEC, + bucketStartTimeNs + 5 * NS_PER_SEC); // Bucket status after screen state change OFF->ON. - screenEvent = CreateScreenStateChangedEvent(bucketStartTimeNs + 15, + screenEvent = CreateScreenStateChangedEvent(bucketStartTimeNs + 15 * NS_PER_SEC, android::view::DisplayStateEnum::DISPLAY_STATE_ON); StateManager::getInstance().onLogEvent(*screenEvent); ASSERT_EQ(3UL, valueProducer->mCurrentSlicedBucket.size()); @@ -4098,6 +4132,8 @@ TEST(ValueMetricProducerTest, TestSlicedState) { it->first.getStateValuesKey().getValues()[0].mValue.int_value); EXPECT_TRUE(it->second.intervals[0].hasValue); EXPECT_EQ(12, it->second.intervals[0].value.long_value); + assertConditionTimer(it->second.conditionTimer, false, 5 * NS_PER_SEC, + bucketStartTimeNs + 15 * NS_PER_SEC); // Value for dimension, state key {{}, ON} it++; EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); @@ -4106,6 +4142,8 @@ TEST(ValueMetricProducerTest, TestSlicedState) { it->first.getStateValuesKey().getValues()[0].mValue.int_value); EXPECT_TRUE(it->second.intervals[0].hasValue); EXPECT_EQ(4, it->second.intervals[0].value.long_value); + assertConditionTimer(it->second.conditionTimer, true, 5 * NS_PER_SEC, + bucketStartTimeNs + 15 * NS_PER_SEC); // Value for dimension, state key {{}, kStateUnknown} it++; EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); @@ -4114,37 +4152,46 @@ TEST(ValueMetricProducerTest, TestSlicedState) { it->first.getStateValuesKey().getValues()[0].mValue.int_value); EXPECT_TRUE(it->second.intervals[0].hasValue); EXPECT_EQ(2, it->second.intervals[0].value.long_value); + assertConditionTimer(it->second.conditionTimer, false, 5 * NS_PER_SEC, + bucketStartTimeNs + 5 * NS_PER_SEC); // Start dump report and check output. ProtoOutputStream output; std::set<string> strSet; - valueProducer->onDumpReport(bucketStartTimeNs + 50, true /* include recent buckets */, true, - NO_TIME_CONSTRAINTS, &strSet, &output); + valueProducer->onDumpReport(bucketStartTimeNs + 50 * NS_PER_SEC, + true /* include recent buckets */, true, NO_TIME_CONSTRAINTS, + &strSet, &output); StatsLogReport report = outputStreamToProto(&output); EXPECT_TRUE(report.has_value_metrics()); ASSERT_EQ(3, report.value_metrics().data_size()); + // {{}, kStateUnknown} auto data = report.value_metrics().data(0); ASSERT_EQ(1, data.bucket_info_size()); EXPECT_EQ(2, report.value_metrics().data(0).bucket_info(0).values(0).value_long()); EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); EXPECT_TRUE(data.slice_by_state(0).has_value()); EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, data.slice_by_state(0).value()); + EXPECT_EQ(5 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos()); + // {{}, ON} data = report.value_metrics().data(1); ASSERT_EQ(1, report.value_metrics().data(1).bucket_info_size()); EXPECT_EQ(13, report.value_metrics().data(1).bucket_info(0).values(0).value_long()); EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); EXPECT_TRUE(data.slice_by_state(0).has_value()); EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON, data.slice_by_state(0).value()); + EXPECT_EQ(40 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos()); + // {{}, OFF} data = report.value_metrics().data(2); ASSERT_EQ(1, report.value_metrics().data(2).bucket_info_size()); EXPECT_EQ(12, report.value_metrics().data(2).bucket_info(0).values(0).value_long()); EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); EXPECT_TRUE(data.slice_by_state(0).has_value()); EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_OFF, data.slice_by_state(0).value()); + EXPECT_EQ(5 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos()); } /* @@ -4169,9 +4216,10 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithMap) { // Screen state change to ON. .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, vector<std::shared_ptr<LogEvent>>* data) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 5); + EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 5 * NS_PER_SEC); data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 5, 5)); + data->push_back( + CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 5 * NS_PER_SEC, 5)); return true; })) // Screen state change to VR has no pull because it is in the same @@ -4183,17 +4231,19 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithMap) { // Screen state change to OFF. .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, vector<std::shared_ptr<LogEvent>>* data) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 15); + EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 15 * NS_PER_SEC); data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 15, 21)); + data->push_back(CreateRepeatedValueLogEvent( + tagId, bucketStartTimeNs + 15 * NS_PER_SEC, 21)); return true; })) // Dump report requested. .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, vector<std::shared_ptr<LogEvent>>* data) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 50); + EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 50 * NS_PER_SEC); data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 50, 30)); + data->push_back(CreateRepeatedValueLogEvent( + tagId, bucketStartTimeNs + 50 * NS_PER_SEC, 30)); return true; })); @@ -4236,12 +4286,13 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithMap) { EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, it->first.getStateValuesKey().getValues()[0].mValue.int_value); EXPECT_FALSE(it->second.intervals[0].hasValue); + assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs); // Bucket status after screen state change kStateUnknown->ON. auto screenEvent = CreateScreenStateChangedEvent( - bucketStartTimeNs + 5, android::view::DisplayStateEnum::DISPLAY_STATE_ON); + bucketStartTimeNs + 5 * NS_PER_SEC, android::view::DisplayStateEnum::DISPLAY_STATE_ON); StateManager::getInstance().onLogEvent(*screenEvent); - ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); + ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size()); // Base for dimension key {} it = valueProducer->mCurrentSlicedBucket.begin(); itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); @@ -4251,20 +4302,29 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithMap) { ASSERT_EQ(1, itBase->second.currentState.getValues().size()); EXPECT_EQ(screenOnGroup.group_id(), itBase->second.currentState.getValues()[0].mValue.long_value); + // Value for dimension, state key {{}, ON GROUP} + EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); + ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); + EXPECT_EQ(screenOnGroup.group_id(), + it->first.getStateValuesKey().getValues()[0].mValue.int_value); + assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 5 * NS_PER_SEC); // Value for dimension, state key {{}, kStateUnknown} + it++; EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, it->first.getStateValuesKey().getValues()[0].mValue.int_value); EXPECT_TRUE(it->second.intervals[0].hasValue); EXPECT_EQ(2, it->second.intervals[0].value.long_value); + assertConditionTimer(it->second.conditionTimer, false, 5 * NS_PER_SEC, + bucketStartTimeNs + 5 * NS_PER_SEC); // Bucket status after screen state change ON->VR. // Both ON and VR are in the same state group, so the base should not change. - screenEvent = CreateScreenStateChangedEvent(bucketStartTimeNs + 10, + screenEvent = CreateScreenStateChangedEvent(bucketStartTimeNs + 10 * NS_PER_SEC, android::view::DisplayStateEnum::DISPLAY_STATE_VR); StateManager::getInstance().onLogEvent(*screenEvent); - ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); + ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size()); // Base for dimension key {} it = valueProducer->mCurrentSlicedBucket.begin(); itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); @@ -4274,20 +4334,29 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithMap) { ASSERT_EQ(1, itBase->second.currentState.getValues().size()); EXPECT_EQ(screenOnGroup.group_id(), itBase->second.currentState.getValues()[0].mValue.int_value); + // Value for dimension, state key {{}, ON GROUP} + EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); + ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); + EXPECT_EQ(screenOnGroup.group_id(), + it->first.getStateValuesKey().getValues()[0].mValue.int_value); + assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 5 * NS_PER_SEC); // Value for dimension, state key {{}, kStateUnknown} + it++; EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, it->first.getStateValuesKey().getValues()[0].mValue.int_value); EXPECT_TRUE(it->second.intervals[0].hasValue); EXPECT_EQ(2, it->second.intervals[0].value.long_value); + assertConditionTimer(it->second.conditionTimer, false, 5 * NS_PER_SEC, + bucketStartTimeNs + 5 * NS_PER_SEC); // Bucket status after screen state change VR->ON. // Both ON and VR are in the same state group, so the base should not change. - screenEvent = CreateScreenStateChangedEvent(bucketStartTimeNs + 12, + screenEvent = CreateScreenStateChangedEvent(bucketStartTimeNs + 12 * NS_PER_SEC, android::view::DisplayStateEnum::DISPLAY_STATE_ON); StateManager::getInstance().onLogEvent(*screenEvent); - ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); + ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size()); // Base for dimension key {} it = valueProducer->mCurrentSlicedBucket.begin(); itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); @@ -4297,19 +4366,28 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithMap) { ASSERT_EQ(1, itBase->second.currentState.getValues().size()); EXPECT_EQ(screenOnGroup.group_id(), itBase->second.currentState.getValues()[0].mValue.int_value); + // Value for dimension, state key {{}, ON GROUP} + EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); + ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); + EXPECT_EQ(screenOnGroup.group_id(), + it->first.getStateValuesKey().getValues()[0].mValue.int_value); + assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 5 * NS_PER_SEC); // Value for dimension, state key {{}, kStateUnknown} + it++; EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, it->first.getStateValuesKey().getValues()[0].mValue.int_value); EXPECT_TRUE(it->second.intervals[0].hasValue); EXPECT_EQ(2, it->second.intervals[0].value.long_value); + assertConditionTimer(it->second.conditionTimer, false, 5 * NS_PER_SEC, + bucketStartTimeNs + 5 * NS_PER_SEC); // Bucket status after screen state change VR->OFF. - screenEvent = CreateScreenStateChangedEvent(bucketStartTimeNs + 15, + screenEvent = CreateScreenStateChangedEvent(bucketStartTimeNs + 15 * NS_PER_SEC, android::view::DisplayStateEnum::DISPLAY_STATE_OFF); StateManager::getInstance().onLogEvent(*screenEvent); - ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size()); + ASSERT_EQ(3UL, valueProducer->mCurrentSlicedBucket.size()); // Base for dimension key {} it = valueProducer->mCurrentSlicedBucket.begin(); itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); @@ -4319,13 +4397,22 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithMap) { ASSERT_EQ(1, itBase->second.currentState.getValues().size()); EXPECT_EQ(screenOffGroup.group_id(), itBase->second.currentState.getValues()[0].mValue.int_value); + // Value for dimension, state key {{}, OFF GROUP} + EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); + ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); + EXPECT_EQ(screenOffGroup.group_id(), + it->first.getStateValuesKey().getValues()[0].mValue.long_value); + assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 15 * NS_PER_SEC); // Value for dimension, state key {{}, ON GROUP} + it++; EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); EXPECT_EQ(screenOnGroup.group_id(), it->first.getStateValuesKey().getValues()[0].mValue.long_value); EXPECT_TRUE(it->second.intervals[0].hasValue); EXPECT_EQ(16, it->second.intervals[0].value.long_value); + assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC, + bucketStartTimeNs + 15 * NS_PER_SEC); // Value for dimension, state key {{}, kStateUnknown} it++; EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); @@ -4334,37 +4421,46 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithMap) { it->first.getStateValuesKey().getValues()[0].mValue.int_value); EXPECT_TRUE(it->second.intervals[0].hasValue); EXPECT_EQ(2, it->second.intervals[0].value.long_value); + assertConditionTimer(it->second.conditionTimer, false, 5 * NS_PER_SEC, + bucketStartTimeNs + 5 * NS_PER_SEC); // Start dump report and check output. ProtoOutputStream output; std::set<string> strSet; - valueProducer->onDumpReport(bucketStartTimeNs + 50, true /* include recent buckets */, true, - NO_TIME_CONSTRAINTS, &strSet, &output); + valueProducer->onDumpReport(bucketStartTimeNs + 50 * NS_PER_SEC, + true /* include recent buckets */, true, NO_TIME_CONSTRAINTS, + &strSet, &output); StatsLogReport report = outputStreamToProto(&output); EXPECT_TRUE(report.has_value_metrics()); ASSERT_EQ(3, report.value_metrics().data_size()); + // {{}, kStateUnknown} auto data = report.value_metrics().data(0); ASSERT_EQ(1, data.bucket_info_size()); EXPECT_EQ(2, report.value_metrics().data(0).bucket_info(0).values(0).value_long()); EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); EXPECT_TRUE(data.slice_by_state(0).has_value()); EXPECT_EQ(-1 /*StateTracker::kStateUnknown*/, data.slice_by_state(0).value()); + EXPECT_EQ(5 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos()); + // {{}, ON GROUP} data = report.value_metrics().data(1); ASSERT_EQ(1, report.value_metrics().data(1).bucket_info_size()); EXPECT_EQ(16, report.value_metrics().data(1).bucket_info(0).values(0).value_long()); EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); EXPECT_TRUE(data.slice_by_state(0).has_group_id()); EXPECT_EQ(screenOnGroup.group_id(), data.slice_by_state(0).group_id()); + EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos()); + // {{}, OFF GROUP} data = report.value_metrics().data(2); ASSERT_EQ(1, report.value_metrics().data(2).bucket_info_size()); EXPECT_EQ(9, report.value_metrics().data(2).bucket_info(0).values(0).value_long()); EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); EXPECT_TRUE(data.slice_by_state(0).has_group_id()); EXPECT_EQ(screenOffGroup.group_id(), data.slice_by_state(0).group_id()); + EXPECT_EQ(35 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos()); } /* @@ -4386,6 +4482,35 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithPrimaryField_WithDimensions) { auto fieldsInState = stateLink->mutable_fields_in_state(); *fieldsInState = CreateDimensions(UID_PROCESS_STATE_ATOM_ID, {1 /* uid */}); + /* + NOTE: "1" denotes uid 1 and "2" denotes uid 2. + bucket # 1 bucket # 2 + 10 20 30 40 50 60 70 80 90 100 110 120 (seconds) + |------------------------------------------|---------------------------------|-- + + (kStateUnknown) + 1 + |-------------| + 20 + + 2 + |----------------------------| + 40 + + (FOREGROUND) + 1 1 + |----------------------------|-------------| |------| + 40 20 10 + + + (BACKGROUND) + 1 + |------------| + 20 + 2 + |-------------|---------------------------------| + 20 50 + */ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _)) // ValueMetricProducer initialized. @@ -4400,64 +4525,64 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithPrimaryField_WithDimensions) { // Uid 1 process state change from kStateUnknown -> Foreground .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, vector<std::shared_ptr<LogEvent>>* data) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 20); + EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 20 * NS_PER_SEC); data->clear(); - data->push_back( - CreateTwoValueLogEvent(tagId, bucketStartTimeNs + 20, 1 /*uid*/, 6)); + data->push_back(CreateTwoValueLogEvent(tagId, bucketStartTimeNs + 20 * NS_PER_SEC, + 1 /*uid*/, 6)); // This event should be skipped. - data->push_back( - CreateTwoValueLogEvent(tagId, bucketStartTimeNs + 20, 2 /*uid*/, 8)); + data->push_back(CreateTwoValueLogEvent(tagId, bucketStartTimeNs + 20 * NS_PER_SEC, + 2 /*uid*/, 8)); return true; })) // Uid 2 process state change from kStateUnknown -> Background .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, vector<std::shared_ptr<LogEvent>>* data) { - EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 40); + EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 40 * NS_PER_SEC); data->clear(); - data->push_back( - CreateTwoValueLogEvent(tagId, bucketStartTimeNs + 40, 2 /*uid*/, 9)); + data->push_back(CreateTwoValueLogEvent(tagId, bucketStartTimeNs + 40 * NS_PER_SEC, + 2 /*uid*/, 9)); // This event should be skipped. - data->push_back( - CreateTwoValueLogEvent(tagId, bucketStartTimeNs + 40, 1 /*uid*/, 12)); + data->push_back(CreateTwoValueLogEvent(tagId, bucketStartTimeNs + 40 * NS_PER_SEC, + 1 /*uid*/, 12)); return true; })) // Uid 1 process state change from Foreground -> Background .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, vector<std::shared_ptr<LogEvent>>* data) { - EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 20); + EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 20 * NS_PER_SEC); data->clear(); - data->push_back( - CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 20, 1 /*uid*/, 13)); + data->push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 20 * NS_PER_SEC, + 1 /*uid*/, 13)); // This event should be skipped. - data->push_back( - CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 20, 2 /*uid*/, 11)); + data->push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 20 * NS_PER_SEC, + 2 /*uid*/, 11)); return true; })) // Uid 1 process state change from Background -> Foreground .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, vector<std::shared_ptr<LogEvent>>* data) { - EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 40); + EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 40 * NS_PER_SEC); data->clear(); - data->push_back( - CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 40, 1 /*uid*/, 17)); + data->push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 40 * NS_PER_SEC, + 1 /*uid*/, 17)); // This event should be skipped. - data->push_back( - CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 40, 2 /*uid */, 15)); + data->push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 40 * NS_PER_SEC, + 2 /*uid */, 15)); return true; })) // Dump report pull. .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, vector<std::shared_ptr<LogEvent>>* data) { - EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 50); + EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 50 * NS_PER_SEC); data->clear(); - data->push_back( - CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 50, 2 /*uid*/, 20)); - data->push_back( - CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 50, 1 /*uid*/, 21)); + data->push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 50 * NS_PER_SEC, + 2 /*uid*/, 20)); + data->push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 50 * NS_PER_SEC, + 1 /*uid*/, 21)); return true; })); @@ -4489,6 +4614,7 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithPrimaryField_WithDimensions) { EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, it->first.getStateValuesKey().getValues()[0].mValue.int_value); EXPECT_FALSE(it->second.intervals[0].hasValue); + assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs); // Base for dimension key {uid 2} it++; itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); @@ -4505,12 +4631,14 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithPrimaryField_WithDimensions) { EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, it->first.getStateValuesKey().getValues()[0].mValue.int_value); EXPECT_FALSE(it->second.intervals[0].hasValue); + assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs); // Bucket status after uid 1 process state change kStateUnknown -> Foreground. - auto uidProcessEvent = CreateUidProcessStateChangedEvent( - bucketStartTimeNs + 20, 1 /* uid */, android::app::PROCESS_STATE_IMPORTANT_FOREGROUND); + auto uidProcessEvent = + CreateUidProcessStateChangedEvent(bucketStartTimeNs + 20 * NS_PER_SEC, 1 /* uid */, + android::app::PROCESS_STATE_IMPORTANT_FOREGROUND); StateManager::getInstance().onLogEvent(*uidProcessEvent); - ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size()); + ASSERT_EQ(3UL, valueProducer->mCurrentSlicedBucket.size()); // Base for dimension key {uid 1}. it = valueProducer->mCurrentSlicedBucket.begin(); itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); @@ -4528,8 +4656,18 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithPrimaryField_WithDimensions) { it->first.getStateValuesKey().getValues()[0].mValue.int_value); EXPECT_TRUE(it->second.intervals[0].hasValue); EXPECT_EQ(3, it->second.intervals[0].value.long_value); + assertConditionTimer(it->second.conditionTimer, false, 20 * NS_PER_SEC, + bucketStartTimeNs + 20 * NS_PER_SEC); + // Value for key {uid 1, FOREGROUND}. + it++; + ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size()); + EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); + ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); + EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, + it->first.getStateValuesKey().getValues()[0].mValue.int_value); + assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 20 * NS_PER_SEC); - // Base for dimension key {uid 2} + // Base for dimension key {uid 2}. it++; itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); EXPECT_TRUE(itBase->second.baseInfos[0].hasBase); @@ -4538,22 +4676,42 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithPrimaryField_WithDimensions) { ASSERT_EQ(1, itBase->second.currentState.getValues().size()); EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, itBase->second.currentState.getValues()[0].mValue.int_value); - // Value for key {uid 2, kStateUnknown} + // Value for key {uid 2, kStateUnknown}. ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size()); EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(-1, it->first.getStateValuesKey().getValues()[0].mValue.int_value); + EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, + it->first.getStateValuesKey().getValues()[0].mValue.int_value); EXPECT_FALSE(it->second.intervals[0].hasValue); + assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs); // Bucket status after uid 2 process state change kStateUnknown -> Background. - uidProcessEvent = CreateUidProcessStateChangedEvent( - bucketStartTimeNs + 40, 2 /* uid */, android::app::PROCESS_STATE_IMPORTANT_BACKGROUND); + uidProcessEvent = + CreateUidProcessStateChangedEvent(bucketStartTimeNs + 40 * NS_PER_SEC, 2 /* uid */, + android::app::PROCESS_STATE_IMPORTANT_BACKGROUND); StateManager::getInstance().onLogEvent(*uidProcessEvent); - ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size()); - // Base for dimension key {uid 1}. + ASSERT_EQ(4UL, valueProducer->mCurrentSlicedBucket.size()); + // Base for dimension key {uid 2}. it = valueProducer->mCurrentSlicedBucket.begin(); itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); EXPECT_TRUE(itBase->second.baseInfos[0].hasBase); + EXPECT_EQ(9, itBase->second.baseInfos[0].base.long_value); + EXPECT_TRUE(itBase->second.hasCurrentState); + ASSERT_EQ(1, itBase->second.currentState.getValues().size()); + EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, + itBase->second.currentState.getValues()[0].mValue.int_value); + // Value for key {uid 2, BACKGROUND}. + ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size()); + EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); + ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); + EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, + it->first.getStateValuesKey().getValues()[0].mValue.int_value); + assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 40 * NS_PER_SEC); + + // Base for dimension key {uid 1}. + it++; + itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); + EXPECT_TRUE(itBase->second.baseInfos[0].hasBase); EXPECT_EQ(6, itBase->second.baseInfos[0].base.long_value); EXPECT_TRUE(itBase->second.hasCurrentState); ASSERT_EQ(1, itBase->second.currentState.getValues().size()); @@ -4563,26 +4721,33 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithPrimaryField_WithDimensions) { ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size()); EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(-1, it->first.getStateValuesKey().getValues()[0].mValue.int_value); + EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, + it->first.getStateValuesKey().getValues()[0].mValue.int_value); EXPECT_TRUE(it->second.intervals[0].hasValue); EXPECT_EQ(3, it->second.intervals[0].value.long_value); + assertConditionTimer(it->second.conditionTimer, false, 20 * NS_PER_SEC, + bucketStartTimeNs + 20 * NS_PER_SEC); - // Base for dimension key {uid 2} + // Value for key {uid 1, FOREGROUND}. it++; - itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); - EXPECT_TRUE(itBase->second.baseInfos[0].hasBase); - EXPECT_EQ(9, itBase->second.baseInfos[0].base.long_value); - EXPECT_TRUE(itBase->second.hasCurrentState); - ASSERT_EQ(1, itBase->second.currentState.getValues().size()); - EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, - itBase->second.currentState.getValues()[0].mValue.int_value); + ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size()); + EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); + ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); + EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, + it->first.getStateValuesKey().getValues()[0].mValue.int_value); + assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 20 * NS_PER_SEC); + // Value for key {uid 2, kStateUnknown} + it++; ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size()); EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); - EXPECT_EQ(-1, it->first.getStateValuesKey().getValues()[0].mValue.int_value); + EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, + it->first.getStateValuesKey().getValues()[0].mValue.int_value); EXPECT_TRUE(it->second.intervals[0].hasValue); EXPECT_EQ(2, it->second.intervals[0].value.long_value); + assertConditionTimer(it->second.conditionTimer, false, 40 * NS_PER_SEC, + bucketStartTimeNs + 40 * NS_PER_SEC); // Pull at end of first bucket. vector<shared_ptr<LogEvent>> allData; @@ -4612,6 +4777,8 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithPrimaryField_WithDimensions) { EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, it->first.getStateValuesKey().getValues()[0].mValue.int_value); EXPECT_FALSE(it->second.intervals[0].hasValue); + assertConditionTimer(it->second.conditionTimer, true, 0, bucket2StartTimeNs); + EXPECT_EQ(20 * NS_PER_SEC, valueProducer->mPastBuckets[it->first][0].mConditionTrueNs); // Base for dimension key {uid 1} it++; @@ -4629,6 +4796,8 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithPrimaryField_WithDimensions) { EXPECT_EQ(-1 /* kStateTracker::kUnknown */, it->first.getStateValuesKey().getValues()[0].mValue.int_value); EXPECT_FALSE(it->second.intervals[0].hasValue); + assertConditionTimer(it->second.conditionTimer, false, 0, bucketStartTimeNs + 20 * NS_PER_SEC); + EXPECT_EQ(20 * NS_PER_SEC, valueProducer->mPastBuckets[it->first][0].mConditionTrueNs); // Value for key {uid 1, FOREGROUND} it++; @@ -4638,6 +4807,8 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithPrimaryField_WithDimensions) { EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, it->first.getStateValuesKey().getValues()[0].mValue.int_value); EXPECT_FALSE(it->second.intervals[0].hasValue); + assertConditionTimer(it->second.conditionTimer, true, 0, bucket2StartTimeNs); + EXPECT_EQ(40 * NS_PER_SEC, valueProducer->mPastBuckets[it->first][0].mConditionTrueNs); // Value for key {uid 2, kStateUnknown} it++; @@ -4647,13 +4818,16 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithPrimaryField_WithDimensions) { EXPECT_EQ(-1 /* kStateTracker::kUnknown */, it->first.getStateValuesKey().getValues()[0].mValue.int_value); EXPECT_FALSE(it->second.intervals[0].hasValue); + assertConditionTimer(it->second.conditionTimer, false, 0, bucketStartTimeNs + 40 * NS_PER_SEC); + EXPECT_EQ(40 * NS_PER_SEC, valueProducer->mPastBuckets[it->first][0].mConditionTrueNs); // Bucket status after uid 1 process state change from Foreground -> Background. - uidProcessEvent = CreateUidProcessStateChangedEvent( - bucket2StartTimeNs + 20, 1 /* uid */, android::app::PROCESS_STATE_IMPORTANT_BACKGROUND); + uidProcessEvent = + CreateUidProcessStateChangedEvent(bucket2StartTimeNs + 20 * NS_PER_SEC, 1 /* uid */, + android::app::PROCESS_STATE_IMPORTANT_BACKGROUND); StateManager::getInstance().onLogEvent(*uidProcessEvent); - ASSERT_EQ(4UL, valueProducer->mCurrentSlicedBucket.size()); + ASSERT_EQ(5UL, valueProducer->mCurrentSlicedBucket.size()); ASSERT_EQ(4UL, valueProducer->mPastBuckets.size()); ASSERT_EQ(2UL, valueProducer->mCurrentBaseInfo.size()); // Base for dimension key {uid 2}. @@ -4672,6 +4846,8 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithPrimaryField_WithDimensions) { EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, it->first.getStateValuesKey().getValues()[0].mValue.int_value); EXPECT_FALSE(it->second.intervals[0].hasValue); + assertConditionTimer(it->second.conditionTimer, true, 0, bucket2StartTimeNs); + // Base for dimension key {uid 1} it++; itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); @@ -4688,6 +4864,17 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithPrimaryField_WithDimensions) { EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, it->first.getStateValuesKey().getValues()[0].mValue.int_value); EXPECT_FALSE(it->second.intervals[0].hasValue); + assertConditionTimer(it->second.conditionTimer, false, 0, bucketStartTimeNs + 20 * NS_PER_SEC); + + // Value for key {uid 1, BACKGROUND} + it++; + ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size()); + EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); + ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); + EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, + it->first.getStateValuesKey().getValues()[0].mValue.int_value); + assertConditionTimer(it->second.conditionTimer, true, 0, bucket2StartTimeNs + 20 * NS_PER_SEC); + // Value for key {uid 1, FOREGROUND} it++; ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size()); @@ -4697,6 +4884,9 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithPrimaryField_WithDimensions) { it->first.getStateValuesKey().getValues()[0].mValue.int_value); EXPECT_TRUE(it->second.intervals[0].hasValue); EXPECT_EQ(3, it->second.intervals[0].value.long_value); + assertConditionTimer(it->second.conditionTimer, false, 20 * NS_PER_SEC, + bucket2StartTimeNs + 20 * NS_PER_SEC); + // Value for key {uid 2, kStateUnknown} it++; ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size()); @@ -4705,10 +4895,12 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithPrimaryField_WithDimensions) { EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, it->first.getStateValuesKey().getValues()[0].mValue.int_value); EXPECT_FALSE(it->second.intervals[0].hasValue); + assertConditionTimer(it->second.conditionTimer, false, 0, bucketStartTimeNs + 40 * NS_PER_SEC); // Bucket status after uid 1 process state change Background->Foreground. - uidProcessEvent = CreateUidProcessStateChangedEvent( - bucket2StartTimeNs + 40, 1 /* uid */, android::app::PROCESS_STATE_IMPORTANT_FOREGROUND); + uidProcessEvent = + CreateUidProcessStateChangedEvent(bucket2StartTimeNs + 40 * NS_PER_SEC, 1 /* uid */, + android::app::PROCESS_STATE_IMPORTANT_FOREGROUND); StateManager::getInstance().onLogEvent(*uidProcessEvent); ASSERT_EQ(5UL, valueProducer->mCurrentSlicedBucket.size()); @@ -4729,6 +4921,7 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithPrimaryField_WithDimensions) { EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, it->first.getStateValuesKey().getValues()[0].mValue.int_value); EXPECT_FALSE(it->second.intervals[0].hasValue); + assertConditionTimer(it->second.conditionTimer, true, 0, bucket2StartTimeNs); // Base for dimension key {uid 1} it++; @@ -4746,6 +4939,7 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithPrimaryField_WithDimensions) { EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, it->first.getStateValuesKey().getValues()[0].mValue.int_value); EXPECT_FALSE(it->second.intervals[0].hasValue); + assertConditionTimer(it->second.conditionTimer, false, 0, bucketStartTimeNs + 20 * NS_PER_SEC); // Value for key {uid 1, BACKGROUND} it++; @@ -4756,6 +4950,8 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithPrimaryField_WithDimensions) { it->first.getStateValuesKey().getValues()[0].mValue.int_value); EXPECT_TRUE(it->second.intervals[0].hasValue); EXPECT_EQ(4, it->second.intervals[0].value.long_value); + assertConditionTimer(it->second.conditionTimer, false, 20 * NS_PER_SEC, + bucket2StartTimeNs + 40 * NS_PER_SEC); // Value for key {uid 1, FOREGROUND} it++; @@ -4766,6 +4962,8 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithPrimaryField_WithDimensions) { it->first.getStateValuesKey().getValues()[0].mValue.int_value); EXPECT_TRUE(it->second.intervals[0].hasValue); EXPECT_EQ(3, it->second.intervals[0].value.long_value); + assertConditionTimer(it->second.conditionTimer, true, 20 * NS_PER_SEC, + bucket2StartTimeNs + 40 * NS_PER_SEC); // Value for key {uid 2, kStateUnknown} it++; @@ -4774,17 +4972,20 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithPrimaryField_WithDimensions) { ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, it->first.getStateValuesKey().getValues()[0].mValue.int_value); + assertConditionTimer(it->second.conditionTimer, false, 0, bucketStartTimeNs + 40 * NS_PER_SEC); // Start dump report and check output. ProtoOutputStream output; std::set<string> strSet; - valueProducer->onDumpReport(bucket2StartTimeNs + 50, true /* include recent buckets */, true, - NO_TIME_CONSTRAINTS, &strSet, &output); + valueProducer->onDumpReport(bucket2StartTimeNs + 50 * NS_PER_SEC, + true /* include recent buckets */, true, NO_TIME_CONSTRAINTS, + &strSet, &output); StatsLogReport report = outputStreamToProto(&output); EXPECT_TRUE(report.has_value_metrics()); ASSERT_EQ(5, report.value_metrics().data_size()); + // {uid 1, BACKGROUND} auto data = report.value_metrics().data(0); ASSERT_EQ(1, data.bucket_info_size()); EXPECT_EQ(4, report.value_metrics().data(0).bucket_info(0).values(0).value_long()); @@ -4792,14 +4993,18 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithPrimaryField_WithDimensions) { EXPECT_TRUE(data.slice_by_state(0).has_value()); EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND, data.slice_by_state(0).value()); + EXPECT_EQ(20 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos()); + // {uid 2, kStateUnknown} data = report.value_metrics().data(1); ASSERT_EQ(1, report.value_metrics().data(1).bucket_info_size()); EXPECT_EQ(2, report.value_metrics().data(1).bucket_info(0).values(0).value_long()); EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); EXPECT_TRUE(data.slice_by_state(0).has_value()); EXPECT_EQ(-1 /*StateTracker::kStateUnknown*/, data.slice_by_state(0).value()); + EXPECT_EQ(40 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos()); + // {uid 1, FOREGROUND} data = report.value_metrics().data(2); EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); EXPECT_TRUE(data.slice_by_state(0).has_value()); @@ -4808,14 +5013,19 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithPrimaryField_WithDimensions) { ASSERT_EQ(2, report.value_metrics().data(2).bucket_info_size()); EXPECT_EQ(4, report.value_metrics().data(2).bucket_info(0).values(0).value_long()); EXPECT_EQ(7, report.value_metrics().data(2).bucket_info(1).values(0).value_long()); + EXPECT_EQ(40 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos()); + EXPECT_EQ(30 * NS_PER_SEC, data.bucket_info(1).condition_true_nanos()); + // {uid 1, kStateUnknown} data = report.value_metrics().data(3); ASSERT_EQ(1, report.value_metrics().data(3).bucket_info_size()); EXPECT_EQ(3, report.value_metrics().data(3).bucket_info(0).values(0).value_long()); EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); EXPECT_TRUE(data.slice_by_state(0).has_value()); EXPECT_EQ(-1 /*StateTracker::kStateUnknown*/, data.slice_by_state(0).value()); + EXPECT_EQ(20 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos()); + // {uid 2, BACKGROUND} data = report.value_metrics().data(4); EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); EXPECT_TRUE(data.slice_by_state(0).has_value()); @@ -4824,6 +5034,1630 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithPrimaryField_WithDimensions) { ASSERT_EQ(2, report.value_metrics().data(4).bucket_info_size()); EXPECT_EQ(6, report.value_metrics().data(4).bucket_info(0).values(0).value_long()); EXPECT_EQ(5, report.value_metrics().data(4).bucket_info(1).values(0).value_long()); + EXPECT_EQ(20 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos()); + EXPECT_EQ(50 * NS_PER_SEC, data.bucket_info(1).condition_true_nanos()); +} + +/* + * Test slicing condition_true_nanos by state for metric that slices by state when data is not + * present in pulled data during a state change. + */ +TEST(ValueMetricProducerTest, TestSlicedStateWithMissingDataInStateChange) { + // Set up ValueMetricProducer. + ValueMetric metric = + ValueMetricProducerTestHelper::createMetricWithState("BATTERY_SAVER_MODE_STATE"); + sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); + /* + NOTE: "-" means that the data was not present in the pulled data. + + bucket # 1 + 10 20 30 40 50 60 (seconds) + |-------------------------------------------------------|-- + x (kStateUnknown) + |-----------| + 10 + + x x (ON) + |---------------------| |-----------| + 20 10 + + - (OFF) + */ + EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _)) + // ValueMetricProducer initialized. + .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, + vector<std::shared_ptr<LogEvent>>* data) { + EXPECT_EQ(eventTimeNs, bucketStartTimeNs); + data->clear(); + data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 3)); + return true; + })) + // Battery saver mode state changed to ON. + .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, + vector<std::shared_ptr<LogEvent>>* data) { + EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10 * NS_PER_SEC); + data->clear(); + data->push_back( + CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 10 * NS_PER_SEC, 5)); + return true; + })) + // Battery saver mode state changed to OFF but data for dimension key {} is not present + // in the pulled data. + .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, + vector<std::shared_ptr<LogEvent>>* data) { + EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 30 * NS_PER_SEC); + data->clear(); + return true; + })) + // Battery saver mode state changed to ON. + .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, + vector<std::shared_ptr<LogEvent>>* data) { + EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 40 * NS_PER_SEC); + data->clear(); + data->push_back( + CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 40 * NS_PER_SEC, 7)); + return true; + })) + // Dump report pull. + .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, + vector<std::shared_ptr<LogEvent>>* data) { + EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 50 * NS_PER_SEC); + data->clear(); + data->push_back(CreateRepeatedValueLogEvent( + tagId, bucketStartTimeNs + 50 * NS_PER_SEC, 15)); + return true; + })); + + StateManager::getInstance().clear(); + sp<ValueMetricProducer> valueProducer = + ValueMetricProducerTestHelper::createValueProducerWithState( + pullerManager, metric, {util::BATTERY_SAVER_MODE_STATE_CHANGED}, {}); + EXPECT_EQ(1, valueProducer->mSlicedStateAtoms.size()); + + // Set up StateManager and check that StateTrackers are initialized. + StateManager::getInstance().registerListener(util::BATTERY_SAVER_MODE_STATE_CHANGED, + valueProducer); + EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount()); + EXPECT_EQ(1, StateManager::getInstance().getListenersCount( + util::BATTERY_SAVER_MODE_STATE_CHANGED)); + + // Bucket status after metric initialized. + ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); + // Base for dimension key {} + auto it = valueProducer->mCurrentSlicedBucket.begin(); + auto itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); + EXPECT_TRUE(itBase->second.hasCurrentState); + ASSERT_EQ(1, itBase->second.currentState.getValues().size()); + EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, + itBase->second.currentState.getValues()[0].mValue.int_value); + // Value for dimension, state key {{}, kStateUnknown} + EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); + ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); + EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, + it->first.getStateValuesKey().getValues()[0].mValue.int_value); + assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs); + + // Bucket status after battery saver mode ON event. + unique_ptr<LogEvent> batterySaverOnEvent = + CreateBatterySaverOnEvent(/*timestamp=*/bucketStartTimeNs + 10 * NS_PER_SEC); + StateManager::getInstance().onLogEvent(*batterySaverOnEvent); + + // Base for dimension key {} + + ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size()); + ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size()); + it = valueProducer->mCurrentSlicedBucket.begin(); + itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); + EXPECT_TRUE(itBase->second.hasCurrentState); + ASSERT_EQ(1, itBase->second.currentState.getValues().size()); + EXPECT_EQ(BatterySaverModeStateChanged::ON, + itBase->second.currentState.getValues()[0].mValue.int_value); + // Value for key {{}, ON} + EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); + ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); + EXPECT_EQ(BatterySaverModeStateChanged::ON, + it->first.getStateValuesKey().getValues()[0].mValue.int_value); + assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 10 * NS_PER_SEC); + + // Value for key {{}, -1} + it++; + EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); + ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); + EXPECT_EQ(-1 /*StateTracker::kUnknown*/, + it->first.getStateValuesKey().getValues()[0].mValue.int_value); + assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC, + bucketStartTimeNs + 10 * NS_PER_SEC); + + // Bucket status after battery saver mode OFF event which is not present + // in the pulled data. + unique_ptr<LogEvent> batterySaverOffEvent = + CreateBatterySaverOffEvent(/*timestamp=*/bucketStartTimeNs + 30 * NS_PER_SEC); + StateManager::getInstance().onLogEvent(*batterySaverOffEvent); + + // Base for dimension key {} + ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size()); + ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size()); + it = valueProducer->mCurrentSlicedBucket.begin(); + itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); + EXPECT_FALSE(itBase->second.hasCurrentState); + ASSERT_EQ(1, itBase->second.currentState.getValues().size()); + EXPECT_EQ(BatterySaverModeStateChanged::ON, + itBase->second.currentState.getValues()[0].mValue.int_value); + // Value for key {{}, ON} + EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); + ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); + EXPECT_EQ(BatterySaverModeStateChanged::ON, + it->first.getStateValuesKey().getValues()[0].mValue.int_value); + assertConditionTimer(it->second.conditionTimer, false, 20 * NS_PER_SEC, + bucketStartTimeNs + 30 * NS_PER_SEC); + + // Value for key {{}, -1} + it++; + EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); + ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); + EXPECT_EQ(-1 /*StateTracker::kUnknown*/, + it->first.getStateValuesKey().getValues()[0].mValue.int_value); + assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC, + bucketStartTimeNs + 10 * NS_PER_SEC); + + // Bucket status after battery saver mode ON event. + batterySaverOnEvent = + CreateBatterySaverOnEvent(/*timestamp=*/bucketStartTimeNs + 40 * NS_PER_SEC); + StateManager::getInstance().onLogEvent(*batterySaverOnEvent); + + // Base for dimension key {} + ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size()); + ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size()); + it = valueProducer->mCurrentSlicedBucket.begin(); + itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); + ASSERT_EQ(1, itBase->second.currentState.getValues().size()); + EXPECT_EQ(BatterySaverModeStateChanged::ON, + itBase->second.currentState.getValues()[0].mValue.int_value); + // Value for key {{}, ON} + EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); + ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); + EXPECT_EQ(BatterySaverModeStateChanged::ON, + it->first.getStateValuesKey().getValues()[0].mValue.int_value); + assertConditionTimer(it->second.conditionTimer, true, 20 * NS_PER_SEC, + bucketStartTimeNs + 40 * NS_PER_SEC); + + // Value for key {{}, -1} + it++; + EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); + ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); + EXPECT_EQ(-1 /*StateTracker::kUnknown*/, + it->first.getStateValuesKey().getValues()[0].mValue.int_value); + assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC, + bucketStartTimeNs + 10 * NS_PER_SEC); + + // Start dump report and check output. + ProtoOutputStream output; + std::set<string> strSet; + valueProducer->onDumpReport(bucketStartTimeNs + 50 * NS_PER_SEC, + true /* include recent buckets */, true, NO_TIME_CONSTRAINTS, + &strSet, &output); + + StatsLogReport report = outputStreamToProto(&output); + EXPECT_TRUE(report.has_value_metrics()); + ASSERT_EQ(2, report.value_metrics().data_size()); + + // {{}, kStateUnknown} + ValueMetricData data = report.value_metrics().data(0); + EXPECT_EQ(util::BATTERY_SAVER_MODE_STATE_CHANGED, data.slice_by_state(0).atom_id()); + EXPECT_EQ(-1 /*StateTracker::kUnknown*/, data.slice_by_state(0).value()); + ASSERT_EQ(1, data.bucket_info_size()); + EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos()); + + // {{}, ON} + data = report.value_metrics().data(1); + EXPECT_EQ(util::BATTERY_SAVER_MODE_STATE_CHANGED, data.slice_by_state(0).atom_id()); + EXPECT_EQ(BatterySaverModeStateChanged::ON, data.slice_by_state(0).value()); + ASSERT_EQ(1, data.bucket_info_size()); + EXPECT_EQ(30 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos()); +} + +/* + * Test for metric that slices by state when data is not present in pulled data + * during an event and then a flush occurs for the current bucket. With the new + * condition timer behavior, a "new" MetricDimensionKey is inserted into + * `mCurrentSlicedBucket` before intervals are closed/added to that new + * MetricDimensionKey. + */ +TEST(ValueMetricProducerTest, TestSlicedStateWithMissingDataThenFlushBucket) { + // Set up ValueMetricProducer. + ValueMetric metric = + ValueMetricProducerTestHelper::createMetricWithState("BATTERY_SAVER_MODE_STATE"); + sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); + /* + NOTE: "-" means that the data was not present in the pulled data. + + bucket # 1 + 10 20 30 40 50 60 (seconds) + |-------------------------------------------------------|-- + - (kStateUnknown) + + - (ON) + */ + EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _)) + // ValueMetricProducer initialized but data for dimension key {} is not present + // in the pulled data.. + .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, + vector<std::shared_ptr<LogEvent>>* data) { + EXPECT_EQ(eventTimeNs, bucketStartTimeNs); + data->clear(); + return true; + })) + // Battery saver mode state changed to ON but data for dimension key {} is not present + // in the pulled data. + .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, + vector<std::shared_ptr<LogEvent>>* data) { + EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10 * NS_PER_SEC); + data->clear(); + return true; + })) + // Dump report pull. + .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, + vector<std::shared_ptr<LogEvent>>* data) { + EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 50 * NS_PER_SEC); + data->clear(); + data->push_back(CreateRepeatedValueLogEvent( + tagId, bucketStartTimeNs + 50 * NS_PER_SEC, 15)); + return true; + })); + + StateManager::getInstance().clear(); + sp<ValueMetricProducer> valueProducer = + ValueMetricProducerTestHelper::createValueProducerWithState( + pullerManager, metric, {util::BATTERY_SAVER_MODE_STATE_CHANGED}, {}); + EXPECT_EQ(1, valueProducer->mSlicedStateAtoms.size()); + + // Set up StateManager and check that StateTrackers are initialized. + StateManager::getInstance().registerListener(util::BATTERY_SAVER_MODE_STATE_CHANGED, + valueProducer); + EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount()); + EXPECT_EQ(1, StateManager::getInstance().getListenersCount( + util::BATTERY_SAVER_MODE_STATE_CHANGED)); + + // Bucket status after metric initialized. + ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size()); + ASSERT_EQ(0UL, valueProducer->mCurrentBaseInfo.size()); + + // Bucket status after battery saver mode ON event which is not present + // in the pulled data. + unique_ptr<LogEvent> batterySaverOnEvent = + CreateBatterySaverOnEvent(/*timestamp=*/bucketStartTimeNs + 10 * NS_PER_SEC); + StateManager::getInstance().onLogEvent(*batterySaverOnEvent); + + ASSERT_EQ(0UL, valueProducer->mCurrentBaseInfo.size()); + ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size()); + + // Start dump report and check output. + ProtoOutputStream output; + std::set<string> strSet; + valueProducer->onDumpReport(bucketStartTimeNs + 50 * NS_PER_SEC, + true /* include recent buckets */, true, NO_TIME_CONSTRAINTS, + &strSet, &output); + + StatsLogReport report = outputStreamToProto(&output); + EXPECT_TRUE(report.has_value_metrics()); + ASSERT_EQ(0, report.value_metrics().data_size()); + ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size()); + ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size()); +} + +TEST(ValueMetricProducerTest, TestSlicedStateWithNoPullOnBucketBoundary) { + // Set up ValueMetricProducer. + ValueMetric metric = + ValueMetricProducerTestHelper::createMetricWithState("BATTERY_SAVER_MODE_STATE"); + sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); + /* + bucket # 1 bucket # 2 + 10 20 30 40 50 60 70 80 90 100 110 120 (seconds) + |------------------------------------|---------------------------|-- + x (kStateUnknown) + |-----| + 10 + x x (ON) + |-----| |-----------| + 10 20 + x (OFF) + |------------------------| + 40 + */ + EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _)) + // ValueMetricProducer initialized. + .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, + vector<std::shared_ptr<LogEvent>>* data) { + EXPECT_EQ(eventTimeNs, bucketStartTimeNs); + data->clear(); + data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 3)); + return true; + })) + // Battery saver mode state changed to ON. + .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, + vector<std::shared_ptr<LogEvent>>* data) { + EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10 * NS_PER_SEC); + data->clear(); + data->push_back( + CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 10 * NS_PER_SEC, 5)); + return true; + })) + // Battery saver mode state changed to OFF. + .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, + vector<std::shared_ptr<LogEvent>>* data) { + EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 20 * NS_PER_SEC); + data->clear(); + data->push_back( + CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 20 * NS_PER_SEC, 7)); + return true; + })) + // Battery saver mode state changed to ON. + .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, + vector<std::shared_ptr<LogEvent>>* data) { + EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 30 * NS_PER_SEC); + data->clear(); + data->push_back(CreateRepeatedValueLogEvent( + tagId, bucket2StartTimeNs + 30 * NS_PER_SEC, 10)); + return true; + })) + // Dump report pull. + .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, + vector<std::shared_ptr<LogEvent>>* data) { + EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 50 * NS_PER_SEC); + data->clear(); + data->push_back(CreateRepeatedValueLogEvent( + tagId, bucket2StartTimeNs + 50 * NS_PER_SEC, 15)); + return true; + })); + + StateManager::getInstance().clear(); + sp<ValueMetricProducer> valueProducer = + ValueMetricProducerTestHelper::createValueProducerWithState( + pullerManager, metric, {util::BATTERY_SAVER_MODE_STATE_CHANGED}, {}); + EXPECT_EQ(1, valueProducer->mSlicedStateAtoms.size()); + + // Set up StateManager and check that StateTrackers are initialized. + StateManager::getInstance().registerListener(util::BATTERY_SAVER_MODE_STATE_CHANGED, + valueProducer); + EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount()); + EXPECT_EQ(1, StateManager::getInstance().getListenersCount( + util::BATTERY_SAVER_MODE_STATE_CHANGED)); + + // Bucket status after metric initialized. + ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); + ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size()); + auto it = valueProducer->mCurrentSlicedBucket.begin(); + auto itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); + EXPECT_TRUE(itBase->second.hasCurrentState); + ASSERT_EQ(1, itBase->second.currentState.getValues().size()); + EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, + itBase->second.currentState.getValues()[0].mValue.int_value); + // Value for dimension, state key {{}, kStateUnknown} + EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); + ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); + EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, + it->first.getStateValuesKey().getValues()[0].mValue.int_value); + assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs); + + // Bucket status after battery saver mode ON event. + unique_ptr<LogEvent> batterySaverOnEvent = + CreateBatterySaverOnEvent(/*timestamp=*/bucketStartTimeNs + 10 * NS_PER_SEC); + StateManager::getInstance().onLogEvent(*batterySaverOnEvent); + + ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size()); + ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size()); + it = valueProducer->mCurrentSlicedBucket.begin(); + itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); + ASSERT_EQ(1, itBase->second.currentState.getValues().size()); + EXPECT_EQ(BatterySaverModeStateChanged::ON, + itBase->second.currentState.getValues()[0].mValue.int_value); + // Value for key {{}, ON} + EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); + ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); + EXPECT_EQ(BatterySaverModeStateChanged::ON, + it->first.getStateValuesKey().getValues()[0].mValue.int_value); + assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 10 * NS_PER_SEC); + + // Value for key {{}, -1} + it++; + EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); + ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); + EXPECT_EQ(-1 /*StateTracker::kUnknown*/, + it->first.getStateValuesKey().getValues()[0].mValue.int_value); + assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC, + bucketStartTimeNs + 10 * NS_PER_SEC); + + // Bucket status after battery saver mode OFF event. + unique_ptr<LogEvent> batterySaverOffEvent = + CreateBatterySaverOffEvent(/*timestamp=*/bucketStartTimeNs + 20 * NS_PER_SEC); + StateManager::getInstance().onLogEvent(*batterySaverOffEvent); + + ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size()); + ASSERT_EQ(3UL, valueProducer->mCurrentSlicedBucket.size()); + it = valueProducer->mCurrentSlicedBucket.begin(); + itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); + ASSERT_EQ(1, itBase->second.currentState.getValues().size()); + EXPECT_EQ(BatterySaverModeStateChanged::OFF, + itBase->second.currentState.getValues()[0].mValue.int_value); + // Value for key {{}, OFF} + EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); + ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); + EXPECT_EQ(BatterySaverModeStateChanged::OFF, + it->first.getStateValuesKey().getValues()[0].mValue.int_value); + assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 20 * NS_PER_SEC); + + // Value for key {{}, ON} + it++; + EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); + ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); + EXPECT_EQ(BatterySaverModeStateChanged::ON, + it->first.getStateValuesKey().getValues()[0].mValue.int_value); + assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC, + bucketStartTimeNs + 20 * NS_PER_SEC); + + // Value for key {{}, -1} + it++; + EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); + ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); + EXPECT_EQ(-1 /*StateTracker::kUnknown*/, + it->first.getStateValuesKey().getValues()[0].mValue.int_value); + assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC, + bucketStartTimeNs + 10 * NS_PER_SEC); + + // Bucket status after battery saver mode ON event. + batterySaverOnEvent = + CreateBatterySaverOnEvent(/*timestamp=*/bucket2StartTimeNs + 30 * NS_PER_SEC); + StateManager::getInstance().onLogEvent(*batterySaverOnEvent); + + ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size()); + ASSERT_EQ(3UL, valueProducer->mCurrentSlicedBucket.size()); + it = valueProducer->mCurrentSlicedBucket.begin(); + itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); + ASSERT_EQ(1, itBase->second.currentState.getValues().size()); + EXPECT_EQ(BatterySaverModeStateChanged::ON, + itBase->second.currentState.getValues()[0].mValue.int_value); + // Value for key {{}, OFF} + EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); + ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); + EXPECT_EQ(BatterySaverModeStateChanged::OFF, + it->first.getStateValuesKey().getValues()[0].mValue.int_value); + assertConditionTimer(it->second.conditionTimer, false, 30 * NS_PER_SEC, + bucket2StartTimeNs + 30 * NS_PER_SEC); + + // Value for key {{}, ON} + it++; + EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); + ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); + EXPECT_EQ(BatterySaverModeStateChanged::ON, + it->first.getStateValuesKey().getValues()[0].mValue.int_value); + assertConditionTimer(it->second.conditionTimer, true, 0, bucket2StartTimeNs + 30 * NS_PER_SEC); + + // Value for key {{}, -1} + it++; + EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); + ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); + EXPECT_EQ(-1 /*StateTracker::kUnknown*/, + it->first.getStateValuesKey().getValues()[0].mValue.int_value); + assertConditionTimer(it->second.conditionTimer, false, 0, bucketStartTimeNs + 10 * NS_PER_SEC); + + // Start dump report and check output. + ProtoOutputStream output; + std::set<string> strSet; + valueProducer->onDumpReport(bucket2StartTimeNs + 50 * NS_PER_SEC, + true /* include recent buckets */, true, NO_TIME_CONSTRAINTS, + &strSet, &output); + + StatsLogReport report = outputStreamToProto(&output); + EXPECT_TRUE(report.has_value_metrics()); + ASSERT_EQ(3, report.value_metrics().data_size()); + + // {{}, kStateUnknown} + ValueMetricData data = report.value_metrics().data(0); + EXPECT_EQ(util::BATTERY_SAVER_MODE_STATE_CHANGED, data.slice_by_state(0).atom_id()); + EXPECT_EQ(-1 /*StateTracker::kUnknown*/, data.slice_by_state(0).value()); + ASSERT_EQ(1, data.bucket_info_size()); + EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos()); + + // {{}, ON} + data = report.value_metrics().data(1); + EXPECT_EQ(util::BATTERY_SAVER_MODE_STATE_CHANGED, data.slice_by_state(0).atom_id()); + EXPECT_EQ(BatterySaverModeStateChanged::ON, data.slice_by_state(0).value()); + ASSERT_EQ(2, data.bucket_info_size()); + EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos()); + EXPECT_EQ(20 * NS_PER_SEC, data.bucket_info(1).condition_true_nanos()); + + // {{}, OFF} + data = report.value_metrics().data(2); + EXPECT_EQ(util::BATTERY_SAVER_MODE_STATE_CHANGED, data.slice_by_state(0).atom_id()); + EXPECT_EQ(BatterySaverModeStateChanged::OFF, data.slice_by_state(0).value()); + ASSERT_EQ(1, data.bucket_info_size()); + EXPECT_EQ(40 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos()); +} + +/* + * Test slicing condition_true_nanos by state for metric that slices by state when data is not + * present in pulled data during a condition change. + */ +TEST(ValueMetricProducerTest, TestSlicedStateWithDataMissingInConditionChange) { + // Set up ValueMetricProducer. + ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithConditionAndState( + "BATTERY_SAVER_MODE_STATE"); + sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); + /* + NOTE: "-" means that the data was not present in the pulled data. + + bucket # 1 + 10 20 30 40 50 60 (seconds) + |-------------------------------------------------------|-- + + T F T (Condition) + x (ON) + |----------------------| - + 20 + */ + EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _)) + // Battery saver mode state changed to ON. + .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, + vector<std::shared_ptr<LogEvent>>* data) { + EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10 * NS_PER_SEC); + data->clear(); + data->push_back( + CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 10 * NS_PER_SEC, 3)); + return true; + })) + // Condition changed to false. + .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, + vector<std::shared_ptr<LogEvent>>* data) { + EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 30 * NS_PER_SEC); + data->clear(); + data->push_back( + CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 30 * NS_PER_SEC, 5)); + return true; + })) + // Condition changed to true but data for dimension key {} is not present in the + // pulled data. + .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, + vector<std::shared_ptr<LogEvent>>* data) { + EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 40 * NS_PER_SEC); + data->clear(); + return true; + })) + // Dump report pull. + .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, + vector<std::shared_ptr<LogEvent>>* data) { + EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 50 * NS_PER_SEC); + data->clear(); + data->push_back(CreateRepeatedValueLogEvent( + tagId, bucketStartTimeNs + 50 * NS_PER_SEC, 20)); + return true; + })); + + StateManager::getInstance().clear(); + sp<ValueMetricProducer> valueProducer = + ValueMetricProducerTestHelper::createValueProducerWithConditionAndState( + pullerManager, metric, {util::BATTERY_SAVER_MODE_STATE_CHANGED}, {}, + ConditionState::kTrue); + EXPECT_EQ(1, valueProducer->mSlicedStateAtoms.size()); + + // Set up StateManager and check that StateTrackers are initialized. + StateManager::getInstance().registerListener(util::BATTERY_SAVER_MODE_STATE_CHANGED, + valueProducer); + EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount()); + EXPECT_EQ(1, StateManager::getInstance().getListenersCount( + util::BATTERY_SAVER_MODE_STATE_CHANGED)); + + // Bucket status after battery saver mode ON event. + unique_ptr<LogEvent> batterySaverOnEvent = + CreateBatterySaverOnEvent(/*timestamp=*/bucketStartTimeNs + 10 * NS_PER_SEC); + StateManager::getInstance().onLogEvent(*batterySaverOnEvent); + // Base for dimension key {} + ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size()); + ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size()); + auto it = valueProducer->mCurrentSlicedBucket.begin(); + auto itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); + EXPECT_TRUE(itBase->second.hasCurrentState); + ASSERT_EQ(1, itBase->second.currentState.getValues().size()); + EXPECT_EQ(BatterySaverModeStateChanged::ON, + itBase->second.currentState.getValues()[0].mValue.int_value); + // Value for key {{}, ON} + EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); + ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); + EXPECT_EQ(BatterySaverModeStateChanged::ON, + it->first.getStateValuesKey().getValues()[0].mValue.int_value); + assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 10 * NS_PER_SEC); + + // Value for key {{}, -1} + it++; + EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); + ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); + EXPECT_EQ(-1 /*StateTracker::kUnknown*/, + it->first.getStateValuesKey().getValues()[0].mValue.int_value); + assertConditionTimer(it->second.conditionTimer, false, 0, 0); + + // Bucket status after condition change to false. + valueProducer->onConditionChanged(false, bucketStartTimeNs + 30 * NS_PER_SEC); + // Base for dimension key {} + ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size()); + ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size()); + it = valueProducer->mCurrentSlicedBucket.begin(); + itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); + EXPECT_TRUE(itBase->second.hasCurrentState); + ASSERT_EQ(1, itBase->second.currentState.getValues().size()); + EXPECT_EQ(BatterySaverModeStateChanged::ON, + itBase->second.currentState.getValues()[0].mValue.int_value); + // Value for key {{}, ON} + EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); + ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); + EXPECT_EQ(BatterySaverModeStateChanged::ON, + it->first.getStateValuesKey().getValues()[0].mValue.int_value); + assertConditionTimer(it->second.conditionTimer, false, 20 * NS_PER_SEC, + bucketStartTimeNs + 30 * NS_PER_SEC); + + // Value for key {{}, -1} + it++; + EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); + ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); + EXPECT_EQ(-1 /*StateTracker::kUnknown*/, + it->first.getStateValuesKey().getValues()[0].mValue.int_value); + assertConditionTimer(it->second.conditionTimer, false, 0, 0); + + // Bucket status after condition change to true. + valueProducer->onConditionChanged(true, bucketStartTimeNs + 40 * NS_PER_SEC); + // Base for dimension key {} + ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size()); + ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size()); + it = valueProducer->mCurrentSlicedBucket.begin(); + itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); + EXPECT_FALSE(itBase->second.hasCurrentState); + ASSERT_EQ(1, itBase->second.currentState.getValues().size()); + EXPECT_EQ(BatterySaverModeStateChanged::ON, + itBase->second.currentState.getValues()[0].mValue.int_value); + // Value for key {{}, ON} + EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); + ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); + EXPECT_EQ(BatterySaverModeStateChanged::ON, + it->first.getStateValuesKey().getValues()[0].mValue.int_value); + assertConditionTimer(it->second.conditionTimer, false, 20 * NS_PER_SEC, + bucketStartTimeNs + 30 * NS_PER_SEC); + + // Value for key {{}, -1} + it++; + EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); + ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); + EXPECT_EQ(-1 /*StateTracker::kUnknown*/, + it->first.getStateValuesKey().getValues()[0].mValue.int_value); + assertConditionTimer(it->second.conditionTimer, false, 0, 0); + + // Start dump report and check output. + ProtoOutputStream output; + std::set<string> strSet; + valueProducer->onDumpReport(bucketStartTimeNs + 50 * NS_PER_SEC, + true /* include recent buckets */, true, NO_TIME_CONSTRAINTS, + &strSet, &output); + + StatsLogReport report = outputStreamToProto(&output); + EXPECT_TRUE(report.has_value_metrics()); + ASSERT_EQ(1, report.value_metrics().data_size()); + + // {{}, ON} + ValueMetricData data = report.value_metrics().data(0); + EXPECT_EQ(util::BATTERY_SAVER_MODE_STATE_CHANGED, data.slice_by_state(0).atom_id()); + EXPECT_EQ(BatterySaverModeStateChanged::ON, data.slice_by_state(0).value()); + ASSERT_EQ(1, data.bucket_info_size()); + EXPECT_EQ(20 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos()); +} + +/* + * Test slicing condition_true_nanos by state for metric that slices by state with a primary field, + * condition, and has multiple dimensions. + */ +TEST(ValueMetricProducerTest, TestSlicedStateWithMultipleDimensions) { + // Set up ValueMetricProducer. + ValueMetric metric = + ValueMetricProducerTestHelper::createMetricWithConditionAndState("UID_PROCESS_STATE"); + metric.mutable_dimensions_in_what()->set_field(tagId); + metric.mutable_dimensions_in_what()->add_child()->set_field(1); + metric.mutable_dimensions_in_what()->add_child()->set_field(3); + + MetricStateLink* stateLink = metric.add_state_link(); + stateLink->set_state_atom_id(UID_PROCESS_STATE_ATOM_ID); + auto fieldsInWhat = stateLink->mutable_fields_in_what(); + *fieldsInWhat = CreateDimensions(tagId, {1 /* uid */}); + auto fieldsInState = stateLink->mutable_fields_in_state(); + *fieldsInState = CreateDimensions(UID_PROCESS_STATE_ATOM_ID, {1 /* uid */}); + + /* + bucket # 1 bucket # 2 + 10 20 30 40 50 60 70 80 90 100 110 120 (seconds) + |------------------------------------------|---------------------------------|-- + + T F T (Condition) + (FOREGROUND) + x {1, 14} + |------| + 10 + + x {1, 16} + |------| + 10 + x {2, 8} + |-------------| + 20 + + (BACKGROUND) + x {1, 14} + |-------------| |----------|---------------------------------| + 20 15 50 + + x {1, 16} + |-------------| |----------|---------------------------------| + 20 15 50 + + x {2, 8} + |----------| |----------|-------------------| + 15 15 30 + */ + sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); + EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _)) + // Uid 1 process state change from kStateUnknown -> Foreground + .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, + vector<std::shared_ptr<LogEvent>>* data) { + EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10 * NS_PER_SEC); + data->clear(); + data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 10 * NS_PER_SEC, + 1 /*uid*/, 3, 14 /*tag*/)); + data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 10 * NS_PER_SEC, + 1 /*uid*/, 3, 16 /*tag*/)); + + // This event should be skipped. + data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 10 * NS_PER_SEC, + 2 /*uid*/, 5, 8 /*tag*/)); + return true; + })) + // Uid 1 process state change from Foreground -> Background + .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, + vector<std::shared_ptr<LogEvent>>* data) { + EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 20 * NS_PER_SEC); + data->clear(); + data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 20 * NS_PER_SEC, + 1 /*uid*/, 5, 14 /*tag*/)); + data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 20 * NS_PER_SEC, + 1 /*uid*/, 5, 16 /*tag*/)); + + // This event should be skipped. + data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 20 * NS_PER_SEC, + 2 /*uid*/, 7, 8 /*tag*/)); + + return true; + })) + // Uid 2 process state change from kStateUnknown -> Background + .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, + vector<std::shared_ptr<LogEvent>>* data) { + EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 25 * NS_PER_SEC); + data->clear(); + data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 25 * NS_PER_SEC, + 2 /*uid*/, 9, 8 /*tag*/)); + + // This event should be skipped. + data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 25 * NS_PER_SEC, + 1 /*uid*/, 9, 14 /* tag */)); + + // This event should be skipped. + data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 25 * NS_PER_SEC, + 1 /*uid*/, 9, 16 /* tag */)); + + return true; + })) + // Condition changed to false. + .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, + vector<std::shared_ptr<LogEvent>>* data) { + EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 40 * NS_PER_SEC); + data->clear(); + data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 40 * NS_PER_SEC, + 1 /*uid*/, 11, 14 /* tag */)); + data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 40 * NS_PER_SEC, + 1 /*uid*/, 11, 16 /* tag */)); + data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 40 * NS_PER_SEC, + 2 /*uid*/, 11, 8 /*tag*/)); + + return true; + })) + // Condition changed to true. + .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, + vector<std::shared_ptr<LogEvent>>* data) { + EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 45 * NS_PER_SEC); + data->clear(); + data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 45 * NS_PER_SEC, + 1 /*uid*/, 13, 14 /* tag */)); + data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 45 * NS_PER_SEC, + 1 /*uid*/, 13, 16 /* tag */)); + data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 45 * NS_PER_SEC, + 2 /*uid*/, 13, 8 /*tag*/)); + return true; + })) + // Uid 2 process state change from Background -> Foreground + .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, + vector<std::shared_ptr<LogEvent>>* data) { + EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 30 * NS_PER_SEC); + data->clear(); + data->push_back(CreateThreeValueLogEvent( + tagId, bucket2StartTimeNs + 30 * NS_PER_SEC, 2 /*uid*/, 18, 8 /*tag*/)); + + // This event should be skipped. + data->push_back(CreateThreeValueLogEvent( + tagId, bucket2StartTimeNs + 30 * NS_PER_SEC, 1 /*uid*/, 18, 14 /* tag */)); + // This event should be skipped. + data->push_back(CreateThreeValueLogEvent( + tagId, bucket2StartTimeNs + 30 * NS_PER_SEC, 1 /*uid*/, 18, 16 /* tag */)); + + return true; + })) + // Dump report pull. + .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs, + vector<std::shared_ptr<LogEvent>>* data) { + EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 50 * NS_PER_SEC); + data->clear(); + data->push_back(CreateThreeValueLogEvent( + tagId, bucket2StartTimeNs + 50 * NS_PER_SEC, 1 /*uid*/, 21, 14 /* tag */)); + data->push_back(CreateThreeValueLogEvent( + tagId, bucket2StartTimeNs + 50 * NS_PER_SEC, 1 /*uid*/, 21, 16 /* tag */)); + data->push_back(CreateThreeValueLogEvent( + tagId, bucket2StartTimeNs + 50 * NS_PER_SEC, 2 /*uid*/, 21, 8 /*tag*/)); + return true; + })); + + StateManager::getInstance().clear(); + sp<ValueMetricProducer> valueProducer = + ValueMetricProducerTestHelper::createValueProducerWithConditionAndState( + pullerManager, metric, {UID_PROCESS_STATE_ATOM_ID}, {}, ConditionState::kTrue); + EXPECT_EQ(1, valueProducer->mSlicedStateAtoms.size()); + + // Set up StateManager and check that StateTrackers are initialized. + StateManager::getInstance().registerListener(UID_PROCESS_STATE_ATOM_ID, valueProducer); + EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount()); + EXPECT_EQ(1, StateManager::getInstance().getListenersCount(UID_PROCESS_STATE_ATOM_ID)); + + // Condition is true. + // Bucket status after uid 1 process state change kStateUnknown -> Foreground. + auto uidProcessEvent = + CreateUidProcessStateChangedEvent(bucketStartTimeNs + 10 * NS_PER_SEC, 1 /* uid */, + android::app::PROCESS_STATE_IMPORTANT_FOREGROUND); + StateManager::getInstance().onLogEvent(*uidProcessEvent); + ASSERT_EQ(2UL, valueProducer->mCurrentBaseInfo.size()); + ASSERT_EQ(4UL, valueProducer->mCurrentSlicedBucket.size()); + // Base for dimension {uid 1, tag 16}. + auto it = valueProducer->mCurrentSlicedBucket.begin(); + auto itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); + ASSERT_EQ(1, itBase->second.currentState.getValues().size()); + EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, + itBase->second.currentState.getValues()[0].mValue.int_value); + // Value for key {{uid 1, uid 16}, FOREGROUND}. + ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size()); + EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); + EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value); + ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); + EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, + it->first.getStateValuesKey().getValues()[0].mValue.int_value); + assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 10 * NS_PER_SEC); + // Value for key {{uid 1, tag 16}, kStateUnknown}. + it++; + ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size()); + EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); + EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value); + ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); + EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, + it->first.getStateValuesKey().getValues()[0].mValue.int_value); + assertConditionTimer(it->second.conditionTimer, false, 0, 0); + + // Base for dimension key {uid 1, tag 14}. + it++; + itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); + ASSERT_EQ(1, itBase->second.currentState.getValues().size()); + EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, + itBase->second.currentState.getValues()[0].mValue.int_value); + // Value for key {{uid 1, tag 14}, FOREGROUND}. + ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size()); + EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); + EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value); + ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); + EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, + it->first.getStateValuesKey().getValues()[0].mValue.int_value); + assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 10 * NS_PER_SEC); + // Value for key {{uid 1, tag 14}, kStateUnknown}. + it++; + ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size()); + EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); + EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value); + ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); + EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, + it->first.getStateValuesKey().getValues()[0].mValue.int_value); + assertConditionTimer(it->second.conditionTimer, false, 0, 0); + + // Bucket status after uid 1 process state change Foreground -> Background. + uidProcessEvent = + CreateUidProcessStateChangedEvent(bucketStartTimeNs + 20 * NS_PER_SEC, 1 /* uid */, + android::app::PROCESS_STATE_IMPORTANT_BACKGROUND); + StateManager::getInstance().onLogEvent(*uidProcessEvent); + ASSERT_EQ(2UL, valueProducer->mCurrentBaseInfo.size()); + ASSERT_EQ(6UL, valueProducer->mCurrentSlicedBucket.size()); + // Base for dimension {uid 1, tag 16}. + it = valueProducer->mCurrentSlicedBucket.begin(); + itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); + ASSERT_EQ(1, itBase->second.currentState.getValues().size()); + EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, + itBase->second.currentState.getValues()[0].mValue.int_value); + // Value for key {{uid 1, uid 16}, BACKGROUND}. + ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size()); + EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); + EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value); + ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); + EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, + it->first.getStateValuesKey().getValues()[0].mValue.int_value); + assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 20 * NS_PER_SEC); + + // Base for dimension key {uid 1, tag 14}. + it++; + itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); + ASSERT_EQ(1, itBase->second.currentState.getValues().size()); + EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, + itBase->second.currentState.getValues()[0].mValue.int_value); + // Value for key {{uid 1, tag 14}, BACKGROUND}. + ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size()); + EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); + EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value); + ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); + EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, + it->first.getStateValuesKey().getValues()[0].mValue.int_value); + assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 20 * NS_PER_SEC); + + // Value for key {{uid 1, uid 16}, FOREGROUND}. + it++; + ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size()); + EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); + EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value); + ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); + EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, + it->first.getStateValuesKey().getValues()[0].mValue.int_value); + assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC, + bucketStartTimeNs + 20 * NS_PER_SEC); + + // Value for key {{uid 1, tag 16}, kStateUnknown}. + it++; + ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size()); + EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); + EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value); + ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); + EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, + it->first.getStateValuesKey().getValues()[0].mValue.int_value); + assertConditionTimer(it->second.conditionTimer, false, 0, 0); + + // Value for key {{uid 1, tag 14}, FOREGROUND}. + it++; + ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size()); + EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); + EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value); + ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); + EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, + it->first.getStateValuesKey().getValues()[0].mValue.int_value); + assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC, + bucketStartTimeNs + 20 * NS_PER_SEC); + + // Value for key {{uid 1, tag 14}, kStateUnknown}. + it++; + ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size()); + EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); + EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value); + ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); + EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, + it->first.getStateValuesKey().getValues()[0].mValue.int_value); + assertConditionTimer(it->second.conditionTimer, false, 0, 0); + + // Bucket status after uid 2 process state change kStateUnknown -> Background. + uidProcessEvent = + CreateUidProcessStateChangedEvent(bucketStartTimeNs + 25 * NS_PER_SEC, 2 /* uid */, + android::app::PROCESS_STATE_IMPORTANT_BACKGROUND); + StateManager::getInstance().onLogEvent(*uidProcessEvent); + ASSERT_EQ(3UL, valueProducer->mCurrentBaseInfo.size()); + ASSERT_EQ(8UL, valueProducer->mCurrentSlicedBucket.size()); + // Base for dimension {uid 2, tag 8}. + it = valueProducer->mCurrentSlicedBucket.begin(); + itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); + ASSERT_EQ(1, itBase->second.currentState.getValues().size()); + EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, + itBase->second.currentState.getValues()[0].mValue.int_value); + // Value for key {{uid 2, uid 8}, BACKGROUND}. + ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size()); + EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); + EXPECT_EQ(8, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value); + ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); + EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, + it->first.getStateValuesKey().getValues()[0].mValue.int_value); + assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 25 * NS_PER_SEC); + + // Value for key {{uid 2, uid 8}, kStateUnknown}. + it++; + ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size()); + EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); + EXPECT_EQ(8, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value); + ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); + EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, + it->first.getStateValuesKey().getValues()[0].mValue.int_value); + assertConditionTimer(it->second.conditionTimer, false, 0, 0); + + // Base for dimension {uid 1, tag 16}. + it++; + itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); + ASSERT_EQ(1, itBase->second.currentState.getValues().size()); + EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, + itBase->second.currentState.getValues()[0].mValue.int_value); + // Value for key {{uid 1, uid 16}, BACKGROUND}. + ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size()); + EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); + EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value); + ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); + EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, + it->first.getStateValuesKey().getValues()[0].mValue.int_value); + assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 20 * NS_PER_SEC); + + // Base for dimension key {uid 1, tag 14}. + it++; + itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); + ASSERT_EQ(1, itBase->second.currentState.getValues().size()); + EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, + itBase->second.currentState.getValues()[0].mValue.int_value); + // Value for key {{uid 1, tag 14}, BACKGROUND}. + ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size()); + EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); + EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value); + ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); + EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, + it->first.getStateValuesKey().getValues()[0].mValue.int_value); + assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 20 * NS_PER_SEC); + + // Value for key {{uid 1, uid 16}, FOREGROUND}. + it++; + ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size()); + EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); + EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value); + ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); + EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, + it->first.getStateValuesKey().getValues()[0].mValue.int_value); + assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC, + bucketStartTimeNs + 20 * NS_PER_SEC); + + // Value for key {{uid 1, tag 16}, kStateUnknown}. + it++; + ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size()); + EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); + EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value); + ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); + EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, + it->first.getStateValuesKey().getValues()[0].mValue.int_value); + assertConditionTimer(it->second.conditionTimer, false, 0, 0); + + // Value for key {{uid 1, tag 14}, FOREGROUND}. + it++; + ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size()); + EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); + EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value); + ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); + EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, + it->first.getStateValuesKey().getValues()[0].mValue.int_value); + assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC, + bucketStartTimeNs + 20 * NS_PER_SEC); + + // Value for key {{uid 1, tag 14}, kStateUnknown}. + it++; + ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size()); + EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); + EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value); + ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); + EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, + it->first.getStateValuesKey().getValues()[0].mValue.int_value); + assertConditionTimer(it->second.conditionTimer, false, 0, 0); + + // Bucket 1 status after condition change to false. + // All condition timers should be turned off. + valueProducer->onConditionChanged(false, bucketStartTimeNs + 40 * NS_PER_SEC); + ASSERT_EQ(3UL, valueProducer->mCurrentBaseInfo.size()); + ASSERT_EQ(8UL, valueProducer->mCurrentSlicedBucket.size()); + // Base for dimension {uid 2, tag 8}. + it = valueProducer->mCurrentSlicedBucket.begin(); + itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); + ASSERT_EQ(1, itBase->second.currentState.getValues().size()); + EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, + itBase->second.currentState.getValues()[0].mValue.int_value); + // Value for key {{uid 2, uid 8}, BACKGROUND}. + ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size()); + EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); + EXPECT_EQ(8, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value); + ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); + EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, + it->first.getStateValuesKey().getValues()[0].mValue.int_value); + assertConditionTimer(it->second.conditionTimer, false, 15 * NS_PER_SEC, + bucketStartTimeNs + 40 * NS_PER_SEC); + + // Value for key {{uid 2, uid 8}, kStateUnknown}. + it++; + ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size()); + EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); + EXPECT_EQ(8, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value); + ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); + EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, + it->first.getStateValuesKey().getValues()[0].mValue.int_value); + assertConditionTimer(it->second.conditionTimer, false, 0, 0); + + // Base for dimension {uid 1, tag 16}. + it++; + itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); + ASSERT_EQ(1, itBase->second.currentState.getValues().size()); + EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, + itBase->second.currentState.getValues()[0].mValue.int_value); + // Value for key {{uid 1, uid 16}, BACKGROUND}. + ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size()); + EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); + EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value); + ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); + EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, + it->first.getStateValuesKey().getValues()[0].mValue.int_value); + assertConditionTimer(it->second.conditionTimer, false, 20 * NS_PER_SEC, + bucketStartTimeNs + 40 * NS_PER_SEC); + + // Base for dimension key {uid 1, tag 14}. + it++; + itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); + ASSERT_EQ(1, itBase->second.currentState.getValues().size()); + EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, + itBase->second.currentState.getValues()[0].mValue.int_value); + // Value for key {{uid 1, tag 14}, BACKGROUND}. + ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size()); + EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); + EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value); + ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); + EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, + it->first.getStateValuesKey().getValues()[0].mValue.int_value); + assertConditionTimer(it->second.conditionTimer, false, 20 * NS_PER_SEC, + bucketStartTimeNs + 40 * NS_PER_SEC); + + // Value for key {{uid 1, uid 16}, FOREGROUND}. + it++; + ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size()); + EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); + EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value); + ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); + EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, + it->first.getStateValuesKey().getValues()[0].mValue.int_value); + assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC, + bucketStartTimeNs + 20 * NS_PER_SEC); + + // Value for key {{uid 1, tag 16}, kStateUnknown}. + it++; + ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size()); + EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); + EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value); + ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); + EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, + it->first.getStateValuesKey().getValues()[0].mValue.int_value); + assertConditionTimer(it->second.conditionTimer, false, 0, 0); + + // Value for key {{uid 1, tag 14}, FOREGROUND}. + it++; + ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size()); + EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); + EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value); + ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); + EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, + it->first.getStateValuesKey().getValues()[0].mValue.int_value); + assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC, + bucketStartTimeNs + 20 * NS_PER_SEC); + + // Value for key {{uid 1, tag 14}, kStateUnknown}. + it++; + ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size()); + EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); + EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value); + ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); + EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, + it->first.getStateValuesKey().getValues()[0].mValue.int_value); + assertConditionTimer(it->second.conditionTimer, false, 0, 0); + + // Bucket 1 status after condition change to true. + valueProducer->onConditionChanged(true, bucketStartTimeNs + 45 * NS_PER_SEC); + ASSERT_EQ(3UL, valueProducer->mCurrentBaseInfo.size()); + ASSERT_EQ(8UL, valueProducer->mCurrentSlicedBucket.size()); + // Base for dimension {uid 2, tag 8}. + it = valueProducer->mCurrentSlicedBucket.begin(); + itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); + ASSERT_EQ(1, itBase->second.currentState.getValues().size()); + EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, + itBase->second.currentState.getValues()[0].mValue.int_value); + // Value for key {{uid 2, uid 8}, BACKGROUND}. + ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size()); + EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); + EXPECT_EQ(8, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value); + ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); + EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, + it->first.getStateValuesKey().getValues()[0].mValue.int_value); + assertConditionTimer(it->second.conditionTimer, true, 15 * NS_PER_SEC, + bucketStartTimeNs + 45 * NS_PER_SEC); + + // Value for key {{uid 2, uid 8}, kStateUnknown}. + it++; + ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size()); + EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); + EXPECT_EQ(8, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value); + ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); + EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, + it->first.getStateValuesKey().getValues()[0].mValue.int_value); + assertConditionTimer(it->second.conditionTimer, false, 0, 0); + + // Base for dimension {uid 1, tag 16}. + it++; + itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); + ASSERT_EQ(1, itBase->second.currentState.getValues().size()); + EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, + itBase->second.currentState.getValues()[0].mValue.int_value); + // Value for key {{uid 1, uid 16}, BACKGROUND}. + ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size()); + EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); + EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value); + ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); + EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, + it->first.getStateValuesKey().getValues()[0].mValue.int_value); + assertConditionTimer(it->second.conditionTimer, true, 20 * NS_PER_SEC, + bucketStartTimeNs + 45 * NS_PER_SEC); + + // Base for dimension key {uid 1, tag 14}. + it++; + itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); + EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, + itBase->second.currentState.getValues()[0].mValue.int_value); + // Value for key {{uid 1, tag 14}, BACKGROUND}. + ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size()); + EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); + EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value); + ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); + EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, + it->first.getStateValuesKey().getValues()[0].mValue.int_value); + assertConditionTimer(it->second.conditionTimer, true, 20 * NS_PER_SEC, + bucketStartTimeNs + 45 * NS_PER_SEC); + + // Value for key {{uid 1, uid 16}, FOREGROUND}. + it++; + ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size()); + EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); + EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value); + ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); + EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, + it->first.getStateValuesKey().getValues()[0].mValue.int_value); + assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC, + bucketStartTimeNs + 20 * NS_PER_SEC); + + // Value for key {{uid 1, tag 16}, kStateUnknown}. + it++; + ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size()); + EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); + EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value); + ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); + EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, + it->first.getStateValuesKey().getValues()[0].mValue.int_value); + assertConditionTimer(it->second.conditionTimer, false, 0, 0); + + // Value for key {{uid 1, tag 14}, FOREGROUND}. + it++; + ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size()); + EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); + EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value); + ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); + EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, + it->first.getStateValuesKey().getValues()[0].mValue.int_value); + assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC, + bucketStartTimeNs + 20 * NS_PER_SEC); + + // Value for key {{uid 1, tag 14}, kStateUnknown}. + it++; + ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size()); + EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); + EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value); + ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); + EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, + it->first.getStateValuesKey().getValues()[0].mValue.int_value); + assertConditionTimer(it->second.conditionTimer, false, 0, 0); + + // Pull at end of first bucket. + vector<shared_ptr<LogEvent>> allData; + allData.push_back( + CreateThreeValueLogEvent(tagId, bucket2StartTimeNs, 1 /*uid*/, 13, 14 /* tag */)); + allData.push_back( + CreateThreeValueLogEvent(tagId, bucket2StartTimeNs, 1 /*uid*/, 13, 16 /* tag */)); + allData.push_back( + CreateThreeValueLogEvent(tagId, bucket2StartTimeNs, 2 /*uid*/, 13, 8 /*tag*/)); + valueProducer->onDataPulled(allData, /** succeeds */ true, bucket2StartTimeNs + 1); + + // Buckets flushed after end of first bucket. + // All condition timers' behavior should rollover to bucket 2. + ASSERT_EQ(8UL, valueProducer->mCurrentSlicedBucket.size()); + ASSERT_EQ(5UL, valueProducer->mPastBuckets.size()); + ASSERT_EQ(3UL, valueProducer->mCurrentBaseInfo.size()); + // Base for dimension {uid 2, tag 8}. + it = valueProducer->mCurrentSlicedBucket.begin(); + itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); + ASSERT_EQ(1, itBase->second.currentState.getValues().size()); + EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, + itBase->second.currentState.getValues()[0].mValue.int_value); + // Value for key {{uid 2, uid 8}, BACKGROUND}. + ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size()); + EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); + EXPECT_EQ(8, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value); + ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); + EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, + it->first.getStateValuesKey().getValues()[0].mValue.int_value); + assertConditionTimer(it->second.conditionTimer, true, 0, bucket2StartTimeNs); + ASSERT_EQ(1, valueProducer->mPastBuckets[it->first].size()); + EXPECT_EQ(30 * NS_PER_SEC, valueProducer->mPastBuckets[it->first][0].mConditionTrueNs); + + // Value for key {{uid 2, uid 8}, kStateUnknown}. + it++; + ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size()); + EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); + EXPECT_EQ(8, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value); + ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); + EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, + it->first.getStateValuesKey().getValues()[0].mValue.int_value); + assertConditionTimer(it->second.conditionTimer, false, 0, 0); + + // Base for dimension {uid 1, tag 16}. + it++; + itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); + ASSERT_EQ(1, itBase->second.currentState.getValues().size()); + EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, + itBase->second.currentState.getValues()[0].mValue.int_value); + // Value for key {{uid 1, uid 16}, BACKGROUND}. + ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size()); + EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); + EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value); + ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); + EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, + it->first.getStateValuesKey().getValues()[0].mValue.int_value); + assertConditionTimer(it->second.conditionTimer, true, 0, bucket2StartTimeNs); + ASSERT_EQ(1, valueProducer->mPastBuckets[it->first].size()); + EXPECT_EQ(35 * NS_PER_SEC, valueProducer->mPastBuckets[it->first][0].mConditionTrueNs); + + // Base for dimension key {uid 1, tag 14}. + it++; + itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); + EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, + itBase->second.currentState.getValues()[0].mValue.int_value); + // Value for key {{uid 1, tag 14}, BACKGROUND}. + ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size()); + EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); + EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value); + ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); + EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, + it->first.getStateValuesKey().getValues()[0].mValue.int_value); + assertConditionTimer(it->second.conditionTimer, true, 0, bucket2StartTimeNs); + ASSERT_EQ(1, valueProducer->mPastBuckets[it->first].size()); + EXPECT_EQ(35 * NS_PER_SEC, valueProducer->mPastBuckets[it->first][0].mConditionTrueNs); + + // Value for key {{uid 1, uid 16}, FOREGROUND}. + it++; + ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size()); + EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); + EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value); + ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); + EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, + it->first.getStateValuesKey().getValues()[0].mValue.int_value); + assertConditionTimer(it->second.conditionTimer, false, 0, bucketStartTimeNs + 20 * NS_PER_SEC); + ASSERT_EQ(1, valueProducer->mPastBuckets[it->first].size()); + EXPECT_EQ(10 * NS_PER_SEC, valueProducer->mPastBuckets[it->first][0].mConditionTrueNs); + + // Value for key {{uid 1, tag 16}, kStateUnknown}. + it++; + ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size()); + EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); + EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value); + ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); + EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, + it->first.getStateValuesKey().getValues()[0].mValue.int_value); + assertConditionTimer(it->second.conditionTimer, false, 0, 0); + + // Value for key {{uid 1, tag 14}, FOREGROUND}. + it++; + ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size()); + EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); + EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value); + ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); + EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, + it->first.getStateValuesKey().getValues()[0].mValue.int_value); + assertConditionTimer(it->second.conditionTimer, false, 0, bucketStartTimeNs + 20 * NS_PER_SEC); + ASSERT_EQ(1, valueProducer->mPastBuckets[it->first].size()); + EXPECT_EQ(10 * NS_PER_SEC, valueProducer->mPastBuckets[it->first][0].mConditionTrueNs); + + // Value for key {{uid 1, tag 14}, kStateUnknown}. + it++; + ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size()); + EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); + EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value); + ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); + EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, + it->first.getStateValuesKey().getValues()[0].mValue.int_value); + assertConditionTimer(it->second.conditionTimer, false, 0, 0); + + // Bucket 2 status after uid 2 process state change Background->Foreground. + uidProcessEvent = + CreateUidProcessStateChangedEvent(bucket2StartTimeNs + 30 * NS_PER_SEC, 2 /* uid */, + android::app::PROCESS_STATE_IMPORTANT_FOREGROUND); + StateManager::getInstance().onLogEvent(*uidProcessEvent); + + ASSERT_EQ(9UL, valueProducer->mCurrentSlicedBucket.size()); + ASSERT_EQ(3UL, valueProducer->mCurrentBaseInfo.size()); + // Base for dimension {uid 2, tag 8}. + it = valueProducer->mCurrentSlicedBucket.begin(); + itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); + ASSERT_EQ(1, itBase->second.currentState.getValues().size()); + EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, + itBase->second.currentState.getValues()[0].mValue.int_value); + // Value for key {{uid 2, uid 8}, FOREGROUND}. + ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size()); + EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); + EXPECT_EQ(8, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value); + ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); + EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, + it->first.getStateValuesKey().getValues()[0].mValue.int_value); + assertConditionTimer(it->second.conditionTimer, true, 0, bucket2StartTimeNs + 30 * NS_PER_SEC); + + // Value for key {{uid 2, uid 8}, BACKGROUND}. + it++; + ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size()); + EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); + EXPECT_EQ(8, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value); + ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); + EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, + it->first.getStateValuesKey().getValues()[0].mValue.int_value); + assertConditionTimer(it->second.conditionTimer, false, 30 * NS_PER_SEC, + bucket2StartTimeNs + 30 * NS_PER_SEC); + + // Value for key {{uid 2, uid 8}, kStateUnknown}. + it++; + ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size()); + EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); + EXPECT_EQ(8, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value); + ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); + EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, + it->first.getStateValuesKey().getValues()[0].mValue.int_value); + assertConditionTimer(it->second.conditionTimer, false, 0, 0); + + // Base for dimension {uid 1, tag 16}. + it++; + itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); + ASSERT_EQ(1, itBase->second.currentState.getValues().size()); + EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, + itBase->second.currentState.getValues()[0].mValue.int_value); + // Value for key {{uid 1, uid 16}, BACKGROUND}. + ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size()); + EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); + EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value); + ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); + EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, + it->first.getStateValuesKey().getValues()[0].mValue.int_value); + assertConditionTimer(it->second.conditionTimer, true, 0, bucket2StartTimeNs); + + // Base for dimension key {uid 1, tag 14}. + it++; + itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); + ASSERT_EQ(1, itBase->second.currentState.getValues().size()); + EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, + itBase->second.currentState.getValues()[0].mValue.int_value); + // Value for key {{uid 1, tag 14}, BACKGROUND}. + ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size()); + EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); + EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value); + ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); + EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, + it->first.getStateValuesKey().getValues()[0].mValue.int_value); + assertConditionTimer(it->second.conditionTimer, true, 0, bucket2StartTimeNs); + + // Value for key {{uid 1, uid 16}, FOREGROUND}. + it++; + ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size()); + EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); + EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value); + ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); + EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, + it->first.getStateValuesKey().getValues()[0].mValue.int_value); + assertConditionTimer(it->second.conditionTimer, false, 0, bucketStartTimeNs + 20 * NS_PER_SEC); + + // Value for key {{uid 1, tag 16}, kStateUnknown}. + it++; + ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size()); + EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); + EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value); + ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); + EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, + it->first.getStateValuesKey().getValues()[0].mValue.int_value); + assertConditionTimer(it->second.conditionTimer, false, 0, 0); + + // Value for key {{uid 1, tag 14}, FOREGROUND}. + it++; + ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size()); + EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); + EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value); + ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); + EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, + it->first.getStateValuesKey().getValues()[0].mValue.int_value); + assertConditionTimer(it->second.conditionTimer, false, 0, bucketStartTimeNs + 20 * NS_PER_SEC); + + // Value for key {{uid 1, tag 14}, kStateUnknown}. + it++; + ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size()); + EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); + EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value); + ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); + EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, + it->first.getStateValuesKey().getValues()[0].mValue.int_value); + assertConditionTimer(it->second.conditionTimer, false, 0, 0); + + // Start dump report and check output. + ProtoOutputStream output; + std::set<string> strSet; + valueProducer->onDumpReport(bucket2StartTimeNs + 50 * NS_PER_SEC, + true /* include recent buckets */, true, NO_TIME_CONSTRAINTS, + &strSet, &output); + + StatsLogReport report = outputStreamToProto(&output); + EXPECT_TRUE(report.has_value_metrics()); + ASSERT_EQ(6, report.value_metrics().data_size()); + + // {{uid 1, tag 14}, FOREGROUND}. + auto data = report.value_metrics().data(0); + EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); + EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND, + data.slice_by_state(0).value()); + ASSERT_EQ(1, data.bucket_info_size()); + EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos()); + + // {{uid 1, tag 16}, BACKGROUND}. + data = report.value_metrics().data(1); + EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); + EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND, + data.slice_by_state(0).value()); + ASSERT_EQ(2, data.bucket_info_size()); + EXPECT_EQ(35 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos()); + EXPECT_EQ(50 * NS_PER_SEC, data.bucket_info(1).condition_true_nanos()); + + // {{uid 1, tag 14}, BACKGROUND}. + data = report.value_metrics().data(2); + EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); + EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND, + data.slice_by_state(0).value()); + ASSERT_EQ(2, data.bucket_info_size()); + EXPECT_EQ(35 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos()); + EXPECT_EQ(50 * NS_PER_SEC, data.bucket_info(1).condition_true_nanos()); + + // {{uid 1, tag 16}, FOREGROUND}. + data = report.value_metrics().data(3); + EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); + EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND, + data.slice_by_state(0).value()); + ASSERT_EQ(1, data.bucket_info_size()); + EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos()); + + // {{uid 2, tag 8}, FOREGROUND}. + data = report.value_metrics().data(4); + EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); + EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND, + data.slice_by_state(0).value()); + ASSERT_EQ(1, data.bucket_info_size()); + EXPECT_EQ(20 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos()); + + // {{uid 2, tag 8}, BACKGROUND}. + data = report.value_metrics().data(5); + EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); + EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND, + data.slice_by_state(0).value()); + ASSERT_EQ(2, data.bucket_info_size()); + EXPECT_EQ(30 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos()); + EXPECT_EQ(30 * NS_PER_SEC, data.bucket_info(1).condition_true_nanos()); } TEST(ValueMetricProducerTest, TestSlicedStateWithCondition) { @@ -4894,15 +6728,23 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithCondition) { ASSERT_EQ(1, itBase->second.currentState.getValues().size()); EXPECT_EQ(BatterySaverModeStateChanged::ON, itBase->second.currentState.getValues()[0].mValue.int_value); - // Value for key {{}, -1} - ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); + // Value for key {{}, ON} + ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size()); std::unordered_map<MetricDimensionKey, ValueMetricProducer::CurrentValueBucket>::iterator it = valueProducer->mCurrentSlicedBucket.begin(); EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); + EXPECT_EQ(BatterySaverModeStateChanged::ON, + it->first.getStateValuesKey().getValues()[0].mValue.int_value); + assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 20 * NS_PER_SEC); + // Value for key {{}, -1} + it++; + EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); + ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); EXPECT_EQ(-1 /*StateTracker::kUnknown*/, it->first.getStateValuesKey().getValues()[0].mValue.int_value); EXPECT_FALSE(it->second.intervals[0].hasValue); + assertConditionTimer(it->second.conditionTimer, false, 0, 0); // Bucket status after battery saver mode OFF event. unique_ptr<LogEvent> batterySaverOffEvent = @@ -4917,15 +6759,27 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithCondition) { ASSERT_EQ(1, itBase->second.currentState.getValues().size()); EXPECT_EQ(BatterySaverModeStateChanged::OFF, itBase->second.currentState.getValues()[0].mValue.int_value); - // Value for key {{}, ON} - ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size()); + // Value for key {{}, OFF} + ASSERT_EQ(3UL, valueProducer->mCurrentSlicedBucket.size()); it = valueProducer->mCurrentSlicedBucket.begin(); EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); + EXPECT_EQ(BatterySaverModeStateChanged::OFF, + it->first.getStateValuesKey().getValues()[0].mValue.int_value); + assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 30 * NS_PER_SEC); + // Value for key {{}, ON} + it++; + EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); + ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); EXPECT_EQ(BatterySaverModeStateChanged::ON, it->first.getStateValuesKey().getValues()[0].mValue.int_value); EXPECT_TRUE(it->second.intervals[0].hasValue); EXPECT_EQ(2, it->second.intervals[0].value.long_value); + assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC, + bucketStartTimeNs + 30 * NS_PER_SEC); + // Value for key {{}, -1} + it++; + assertConditionTimer(it->second.conditionTimer, false, 0, 0); // Pull at end of first bucket. vector<shared_ptr<LogEvent>> allData; @@ -4944,6 +6798,15 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithCondition) { ASSERT_EQ(1, itBase->second.currentState.getValues().size()); EXPECT_EQ(BatterySaverModeStateChanged::OFF, itBase->second.currentState.getValues()[0].mValue.int_value); + // Value for key {{}, OFF} + it = valueProducer->mCurrentSlicedBucket.begin(); + assertConditionTimer(it->second.conditionTimer, true, 0, bucket2StartTimeNs); + // Value for key {{}, ON} + it++; + assertConditionTimer(it->second.conditionTimer, false, 0, bucketStartTimeNs + 30 * NS_PER_SEC); + // Value for key {{}, -1} + it++; + assertConditionTimer(it->second.conditionTimer, false, 0, 0); // Bucket 2 status after condition change to false. valueProducer->onConditionChanged(false, bucket2StartTimeNs + 10 * NS_PER_SEC); @@ -4964,6 +6827,19 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithCondition) { it->first.getStateValuesKey().getValues()[0].mValue.int_value); EXPECT_TRUE(it->second.intervals[0].hasValue); EXPECT_EQ(4, it->second.intervals[0].value.long_value); + assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC, + bucket2StartTimeNs + 10 * NS_PER_SEC); + // Value for key {{}, ON} + it++; + EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size()); + ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size()); + EXPECT_EQ(BatterySaverModeStateChanged::ON, + it->first.getStateValuesKey().getValues()[0].mValue.int_value); + EXPECT_FALSE(it->second.intervals[0].hasValue); + assertConditionTimer(it->second.conditionTimer, false, 0, bucketStartTimeNs + 30 * NS_PER_SEC); + // Value for key {{}, -1} + it++; + assertConditionTimer(it->second.conditionTimer, false, 0, 0); // Start dump report and check output. ProtoOutputStream output; @@ -4982,6 +6858,7 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithCondition) { EXPECT_EQ(BatterySaverModeStateChanged::ON, data.slice_by_state(0).value()); ASSERT_EQ(1, data.bucket_info_size()); EXPECT_EQ(2, data.bucket_info(0).values(0).value_long()); + EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos()); data = report.value_metrics().data(1); EXPECT_EQ(util::BATTERY_SAVER_MODE_STATE_CHANGED, data.slice_by_state(0).atom_id()); @@ -4990,6 +6867,8 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithCondition) { ASSERT_EQ(2, data.bucket_info_size()); EXPECT_EQ(6, data.bucket_info(0).values(0).value_long()); EXPECT_EQ(4, data.bucket_info(1).values(0).value_long()); + EXPECT_EQ(30 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos()); + EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(1).condition_true_nanos()); } /* diff --git a/cmds/statsd/tests/metrics/parsing_utils/config_update_utils_test.cpp b/cmds/statsd/tests/metrics/parsing_utils/config_update_utils_test.cpp index 076000fab80f..65e5875e39bf 100644 --- a/cmds/statsd/tests/metrics/parsing_utils/config_update_utils_test.cpp +++ b/cmds/statsd/tests/metrics/parsing_utils/config_update_utils_test.cpp @@ -920,14 +920,13 @@ TEST_F(ConfigUpdateTest, TestEventMetricPreserve) { // Create an initial config. EXPECT_TRUE(initConfig(config)); - set<int64_t> replacedMatchers; - set<int64_t> replacedConditions; unordered_map<int64_t, int> metricToActivationMap; - UpdateStatus status = UPDATE_UNKNOWN; - EXPECT_TRUE(determineEventMetricUpdateStatus(config, *metric, oldMetricProducerMap, - oldMetricProducers, metricToActivationMap, - replacedMatchers, replacedConditions, status)); - EXPECT_EQ(status, UPDATE_PRESERVE); + vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN); + EXPECT_TRUE(determineAllMetricUpdateStatuses(config, oldMetricProducerMap, oldMetricProducers, + metricToActivationMap, + /*replacedMatchers*/ {}, /*replacedConditions=*/{}, + /*replacedStates=*/{}, metricsToUpdate)); + EXPECT_EQ(metricsToUpdate[0], UPDATE_PRESERVE); } TEST_F(ConfigUpdateTest, TestEventMetricActivationAdded) { @@ -957,14 +956,13 @@ TEST_F(ConfigUpdateTest, TestEventMetricActivationAdded) { eventActivation->set_atom_matcher_id(startMatcher.id()); eventActivation->set_ttl_seconds(5); - set<int64_t> replacedMatchers; - set<int64_t> replacedConditions; unordered_map<int64_t, int> metricToActivationMap = {{12345, 0}}; - UpdateStatus status = UPDATE_UNKNOWN; - EXPECT_TRUE(determineEventMetricUpdateStatus(config, *metric, oldMetricProducerMap, - oldMetricProducers, metricToActivationMap, - replacedMatchers, replacedConditions, status)); - EXPECT_EQ(status, UPDATE_REPLACE); + vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN); + EXPECT_TRUE(determineAllMetricUpdateStatuses(config, oldMetricProducerMap, oldMetricProducers, + metricToActivationMap, + /*replacedMatchers*/ {}, /*replacedConditions=*/{}, + /*replacedStates=*/{}, metricsToUpdate)); + EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE); } TEST_F(ConfigUpdateTest, TestEventMetricWhatChanged) { @@ -987,14 +985,13 @@ TEST_F(ConfigUpdateTest, TestEventMetricWhatChanged) { // Create an initial config. EXPECT_TRUE(initConfig(config)); - set<int64_t> replacedMatchers = {whatMatcher.id()}; - set<int64_t> replacedConditions; unordered_map<int64_t, int> metricToActivationMap; - UpdateStatus status = UPDATE_UNKNOWN; - EXPECT_TRUE(determineEventMetricUpdateStatus(config, *metric, oldMetricProducerMap, - oldMetricProducers, metricToActivationMap, - replacedMatchers, replacedConditions, status)); - EXPECT_EQ(status, UPDATE_REPLACE); + vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN); + EXPECT_TRUE(determineAllMetricUpdateStatuses( + config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap, + /*replacedMatchers*/ {whatMatcher.id()}, /*replacedConditions=*/{}, + /*replacedStates=*/{}, metricsToUpdate)); + EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE); } TEST_F(ConfigUpdateTest, TestEventMetricConditionChanged) { @@ -1017,14 +1014,13 @@ TEST_F(ConfigUpdateTest, TestEventMetricConditionChanged) { // Create an initial config. EXPECT_TRUE(initConfig(config)); - set<int64_t> replacedMatchers; - set<int64_t> replacedConditions = {predicate.id()}; unordered_map<int64_t, int> metricToActivationMap; - UpdateStatus status = UPDATE_UNKNOWN; - EXPECT_TRUE(determineEventMetricUpdateStatus(config, *metric, oldMetricProducerMap, - oldMetricProducers, metricToActivationMap, - replacedMatchers, replacedConditions, status)); - EXPECT_EQ(status, UPDATE_REPLACE); + vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN); + EXPECT_TRUE(determineAllMetricUpdateStatuses( + config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap, + /*replacedMatchers*/ {}, /*replacedConditions=*/{predicate.id()}, + /*replacedStates=*/{}, metricsToUpdate)); + EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE); } TEST_F(ConfigUpdateTest, TestMetricConditionLinkDepsChanged) { @@ -1054,14 +1050,13 @@ TEST_F(ConfigUpdateTest, TestMetricConditionLinkDepsChanged) { // Create an initial config. EXPECT_TRUE(initConfig(config)); - set<int64_t> replacedMatchers; - set<int64_t> replacedConditions = {linkPredicate.id()}; unordered_map<int64_t, int> metricToActivationMap; - UpdateStatus status = UPDATE_UNKNOWN; - EXPECT_TRUE(determineEventMetricUpdateStatus(config, *metric, oldMetricProducerMap, - oldMetricProducers, metricToActivationMap, - replacedMatchers, replacedConditions, status)); - EXPECT_EQ(status, UPDATE_REPLACE); + vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN); + EXPECT_TRUE(determineAllMetricUpdateStatuses( + config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap, + /*replacedMatchers*/ {}, /*replacedConditions=*/{linkPredicate.id()}, + /*replacedStates=*/{}, metricsToUpdate)); + EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE); } TEST_F(ConfigUpdateTest, TestEventMetricActivationDepsChange) { @@ -1090,14 +1085,13 @@ TEST_F(ConfigUpdateTest, TestEventMetricActivationDepsChange) { // Create an initial config. EXPECT_TRUE(initConfig(config)); - set<int64_t> replacedMatchers = {startMatcher.id()}; // The activation matcher is replaced. - set<int64_t> replacedConditions; unordered_map<int64_t, int> metricToActivationMap = {{12345, 0}}; - UpdateStatus status = UPDATE_UNKNOWN; - EXPECT_TRUE(determineEventMetricUpdateStatus(config, *metric, oldMetricProducerMap, - oldMetricProducers, metricToActivationMap, - replacedMatchers, replacedConditions, status)); - EXPECT_EQ(status, UPDATE_REPLACE); + vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN); + EXPECT_TRUE(determineAllMetricUpdateStatuses( + config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap, + /*replacedMatchers*/ {startMatcher.id()}, /*replacedConditions=*/{}, + /*replacedStates=*/{}, metricsToUpdate)); + EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE); } TEST_F(ConfigUpdateTest, TestUpdateEventMetrics) { diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java index a2d0b892aa0a..1f8cf8ac6d1d 100644 --- a/core/java/android/app/ActivityManagerInternal.java +++ b/core/java/android/app/ActivityManagerInternal.java @@ -46,39 +46,20 @@ public abstract class ActivityManagerInternal { // Access modes for handleIncomingUser. - /** - * Allows access to a caller with {@link android.Manifest.permission#INTERACT_ACROSS_USERS} or - * {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL}. - */ public static final int ALLOW_NON_FULL = 0; /** * Allows access to a caller with {@link android.Manifest.permission#INTERACT_ACROSS_USERS} - * or {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL} if in the same profile - * group. + * if in the same profile group. * Otherwise, {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL} is required. */ - public static final int ALLOW_NON_FULL_IN_PROFILE_OR_FULL = 1; - /** - * Allows access to a caller with {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL} - * only. - */ + public static final int ALLOW_NON_FULL_IN_PROFILE = 1; public static final int ALLOW_FULL_ONLY = 2; /** * Allows access to a caller with {@link android.Manifest.permission#INTERACT_ACROSS_PROFILES} - * or {@link android.Manifest.permission#INTERACT_ACROSS_USERS} or - * {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL} if in the same profile group. + * or {@link android.Manifest.permission#INTERACT_ACROSS_USERS} if in the same profile group. * Otherwise, {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL} is required. */ - public static final int ALLOW_ACROSS_PROFILES_IN_PROFILE_OR_FULL = 3; - /** - * Requires {@link android.Manifest.permission#INTERACT_ACROSS_PROFILES}, - * {@link android.Manifest.permission#INTERACT_ACROSS_USERS}, or - * {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL} if in same profile group, - * otherwise {@link android.Manifest.permission#INTERACT_ACROSS_USERS} or - * {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL}. (so this is an extension - * to {@link #ALLOW_NON_FULL}) - */ - public static final int ALLOW_ACROSS_PROFILES_IN_PROFILE_OR_NON_FULL = 4; + public static final int ALLOW_ALL_PROFILE_PERMISSIONS_IN_PROFILE = 3; /** * Verify that calling app has access to the given provider. diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index 04f72f6dc71d..167b5a8029c0 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -6741,14 +6741,10 @@ public class AppOpsManager { */ @RequiresPermission(android.Manifest.permission.MANAGE_APP_OPS_MODES) public void setUidMode(int code, int uid, @Mode int mode) { - // Clear calling UID to handle calls from inside the system server. See #noteOpNoThrow - long token = Binder.clearCallingIdentity(); try { mService.setUidMode(code, uid, mode); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); - } finally { - Binder.restoreCallingIdentity(token); } } @@ -6766,7 +6762,11 @@ public class AppOpsManager { @TestApi @RequiresPermission(android.Manifest.permission.MANAGE_APP_OPS_MODES) public void setUidMode(@NonNull String appOp, int uid, @Mode int mode) { - setUidMode(AppOpsManager.strOpToOp(appOp), uid, mode); + try { + mService.setUidMode(AppOpsManager.strOpToOp(appOp), uid, mode); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } } /** @hide */ @@ -6795,14 +6795,10 @@ public class AppOpsManager { @TestApi @RequiresPermission(android.Manifest.permission.MANAGE_APP_OPS_MODES) public void setMode(int code, int uid, String packageName, @Mode int mode) { - // Clear calling UID to handle calls from inside the system server. See #noteOpNoThrow - long token = Binder.clearCallingIdentity(); try { mService.setMode(code, uid, packageName, mode); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); - } finally { - Binder.restoreCallingIdentity(token); } } @@ -6822,7 +6818,11 @@ public class AppOpsManager { @RequiresPermission(android.Manifest.permission.MANAGE_APP_OPS_MODES) public void setMode(@NonNull String op, int uid, @Nullable String packageName, @Mode int mode) { - setMode(strOpToOp(op), uid, packageName, mode); + try { + mService.setMode(strOpToOp(op), uid, packageName, mode); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } } /** @@ -7298,14 +7298,10 @@ public class AppOpsManager { * @hide */ public int unsafeCheckOpRawNoThrow(int op, int uid, @NonNull String packageName) { - // Clear calling UID to handle calls from inside the system server. See #noteOpNoThrow - long token = Binder.clearCallingIdentity(); try { return mService.checkOperationRaw(op, uid, packageName); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); - } finally { - Binder.restoreCallingIdentity(token); } } @@ -7477,20 +7473,8 @@ public class AppOpsManager { } } - int mode; - // Making the binder call "noteOperation" usually sets Binder.callingUid to the calling - // processes UID. Hence clearing the calling UID is superfluous. - // If the call is inside the system server though "noteOperation" is not a binder all, - // it is only a method call. Hence Binder.callingUid might still be set to the app that - // called the system server. This can lead to problems as not every app can see the - // same appops the system server can see. - long token = Binder.clearCallingIdentity(); - try { - mode = mService.noteOperation(op, uid, packageName, attributionTag, - collectionMode == COLLECT_ASYNC, message, shouldCollectMessage); - } finally { - Binder.restoreCallingIdentity(token); - } + int mode = mService.noteOperation(op, uid, packageName, attributionTag, + collectionMode == COLLECT_ASYNC, message, shouldCollectMessage); if (mode == MODE_ALLOWED) { if (collectionMode == COLLECT_SELF) { @@ -7653,17 +7637,10 @@ public class AppOpsManager { } } - int mode; - // Clear calling UID to handle calls from inside the system server. See #noteOpNoThrow - long token = Binder.clearCallingIdentity(); - try { - mode = mService.noteProxyOperation(op, proxiedUid, proxiedPackageName, - proxiedAttributionTag, myUid, mContext.getOpPackageName(), - mContext.getAttributionTag(), collectionMode == COLLECT_ASYNC, message, - shouldCollectMessage); - } finally { - Binder.restoreCallingIdentity(token); - } + int mode = mService.noteProxyOperation(op, proxiedUid, proxiedPackageName, + proxiedAttributionTag, myUid, mContext.getOpPackageName(), + mContext.getAttributionTag(), collectionMode == COLLECT_ASYNC, message, + shouldCollectMessage); if (mode == MODE_ALLOWED) { if (collectionMode == COLLECT_SELF) { @@ -7713,8 +7690,6 @@ public class AppOpsManager { */ @UnsupportedAppUsage public int checkOp(int op, int uid, String packageName) { - // Clear calling UID to handle calls from inside the system server. See #noteOpNoThrow - long token = Binder.clearCallingIdentity(); try { int mode = mService.checkOperation(op, uid, packageName); if (mode == MODE_ERRORED) { @@ -7723,8 +7698,6 @@ public class AppOpsManager { return mode; } catch (RemoteException e) { throw e.rethrowFromSystemServer(); - } finally { - Binder.restoreCallingIdentity(token); } } @@ -7735,15 +7708,11 @@ public class AppOpsManager { */ @UnsupportedAppUsage public int checkOpNoThrow(int op, int uid, String packageName) { - // Clear calling UID to handle calls from inside the system server. See #noteOpNoThrow - long token = Binder.clearCallingIdentity(); try { int mode = mService.checkOperation(op, uid, packageName); return mode == AppOpsManager.MODE_FOREGROUND ? AppOpsManager.MODE_ALLOWED : mode; } catch (RemoteException e) { throw e.rethrowFromSystemServer(); - } finally { - Binder.restoreCallingIdentity(token); } } @@ -7995,16 +7964,9 @@ public class AppOpsManager { } } - int mode; - // Clear calling UID to handle calls from inside the system server. See #noteOpNoThrow - long token = Binder.clearCallingIdentity(); - try { - mode = mService.startOperation(getClientId(), op, uid, packageName, - attributionTag, startIfModeDefault, collectionMode == COLLECT_ASYNC, - message, shouldCollectMessage); - } finally { - Binder.restoreCallingIdentity(token); - } + int mode = mService.startOperation(getClientId(), op, uid, packageName, + attributionTag, startIfModeDefault, collectionMode == COLLECT_ASYNC, message, + shouldCollectMessage); if (mode == MODE_ALLOWED) { if (collectionMode == COLLECT_SELF) { @@ -8067,14 +8029,10 @@ public class AppOpsManager { */ public void finishOp(int op, int uid, @NonNull String packageName, @Nullable String attributionTag) { - // Clear calling UID to handle calls from inside the system server. See #noteOpNoThrow - long token = Binder.clearCallingIdentity(); try { mService.finishOperation(getClientId(), op, uid, packageName, attributionTag); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); - } finally { - Binder.restoreCallingIdentity(token); } } @@ -8666,14 +8624,10 @@ public class AppOpsManager { // TODO: Uncomment below annotation once b/73559440 is fixed // @RequiresPermission(value=Manifest.permission.WATCH_APPOPS, conditional=true) public boolean isOperationActive(int code, int uid, String packageName) { - // Clear calling UID to handle calls from inside the system server. See #noteOpNoThrow - long token = Binder.clearCallingIdentity(); try { return mService.isOperationActive(code, uid, packageName); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); - } finally { - Binder.restoreCallingIdentity(token); } } diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java index cd352e141994..fbc87ce0cace 100644 --- a/core/java/android/app/PendingIntent.java +++ b/core/java/android/app/PendingIntent.java @@ -19,12 +19,16 @@ package android.app; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.compat.Compatibility; +import android.compat.annotation.ChangeId; +import android.compat.annotation.EnabledAfter; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.content.IIntentReceiver; import android.content.IIntentSender; import android.content.Intent; import android.content.IntentSender; +import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; @@ -35,6 +39,7 @@ import android.os.RemoteException; import android.os.UserHandle; import android.util.AndroidException; import android.util.ArraySet; +import android.util.Log; import android.util.proto.ProtoOutputStream; import com.android.internal.os.IResultReceiver; @@ -102,11 +107,20 @@ import java.lang.annotation.RetentionPolicy; * FLAG_ONE_SHOT, <b>both</b> FLAG_ONE_SHOT and FLAG_NO_CREATE need to be supplied. */ public final class PendingIntent implements Parcelable { + private static final String TAG = "PendingIntent"; private final IIntentSender mTarget; private IResultReceiver mCancelReceiver; private IBinder mWhitelistToken; private ArraySet<CancelListener> mCancelListeners; + /** + * It is now required to specify either {@link #FLAG_IMMUTABLE} + * or {@link #FLAG_MUTABLE} when creating a PendingIntent. + */ + @ChangeId + @EnabledAfter(targetSdkVersion = android.os.Build.VERSION_CODES.R) + static final long PENDING_INTENT_EXPLICIT_MUTABILITY_REQUIRED = 160794467L; + /** @hide */ @IntDef(flag = true, value = { @@ -115,6 +129,7 @@ public final class PendingIntent implements Parcelable { FLAG_CANCEL_CURRENT, FLAG_UPDATE_CURRENT, FLAG_IMMUTABLE, + FLAG_MUTABLE, Intent.FILL_IN_ACTION, Intent.FILL_IN_DATA, @@ -175,6 +190,20 @@ public final class PendingIntent implements Parcelable { public static final int FLAG_IMMUTABLE = 1<<26; /** + * Flag indicating that the created PendingIntent should be mutable. + * This flag cannot be combined with {@link #FLAG_IMMUTABLE}. <p>Up until + * {@link android.os.Build.VERSION_CODES#R}, PendingIntents are assumed to + * be mutable by default, unless {@link #FLAG_IMMUTABLE} is set. Starting + * with {@link android.os.Build.VERSION_CODES#S}, it will be required to + * explicitly specify the mutability of PendingIntents on creation with + * either (@link #FLAG_IMMUTABLE} or {@link #FLAG_MUTABLE}. It is strongly + * recommended to use {@link #FLAG_IMMUTABLE} when creating a + * PendingIntent. {@link #FLAG_MUTABLE} should only be used when some + * functionality relies on modifying the underlying intent. + */ + public static final int FLAG_MUTABLE = 1<<25; + + /** * Exception thrown when trying to send through a PendingIntent that * has been canceled or is otherwise no longer able to execute the request. */ @@ -286,6 +315,23 @@ public final class PendingIntent implements Parcelable { sOnMarshaledListener.set(listener); } + private static void checkFlags(int flags, String packageName) { + final boolean flagImmutableSet = (flags & PendingIntent.FLAG_IMMUTABLE) != 0; + final boolean flagMutableSet = (flags & PendingIntent.FLAG_MUTABLE) != 0; + + if (flagImmutableSet && flagMutableSet) { + throw new IllegalArgumentException( + "Cannot set both FLAG_IMMUTABLE and FLAG_MUTABLE for PendingIntent"); + } + + if (Compatibility.isChangeEnabled(PENDING_INTENT_EXPLICIT_MUTABILITY_REQUIRED) + && !flagImmutableSet && !flagMutableSet) { + Log.wtf(TAG, packageName + ": Targeting S+ (version " + Build.VERSION_CODES.S + + " and above) requires that one of FLAG_IMMUTABLE or FLAG_MUTABLE" + + " be specified when creating a PendingIntent"); + } + } + /** * Retrieve a PendingIntent that will start a new activity, like calling * {@link Context#startActivity(Intent) Context.startActivity(Intent)}. @@ -350,6 +396,7 @@ public final class PendingIntent implements Parcelable { String packageName = context.getPackageName(); String resolvedType = intent != null ? intent.resolveTypeIfNeeded( context.getContentResolver()) : null; + checkFlags(flags, packageName); try { intent.migrateExtraStreamToClipData(context); intent.prepareToLeaveProcess(context); @@ -376,6 +423,7 @@ public final class PendingIntent implements Parcelable { String packageName = context.getPackageName(); String resolvedType = intent != null ? intent.resolveTypeIfNeeded( context.getContentResolver()) : null; + checkFlags(flags, packageName); try { intent.migrateExtraStreamToClipData(context); intent.prepareToLeaveProcess(context); @@ -495,6 +543,7 @@ public final class PendingIntent implements Parcelable { intents[i].prepareToLeaveProcess(context); resolvedTypes[i] = intents[i].resolveTypeIfNeeded(context.getContentResolver()); } + checkFlags(flags, packageName); try { IIntentSender target = ActivityManager.getService().getIntentSenderWithFeature( @@ -521,6 +570,7 @@ public final class PendingIntent implements Parcelable { intents[i].prepareToLeaveProcess(context); resolvedTypes[i] = intents[i].resolveTypeIfNeeded(context.getContentResolver()); } + checkFlags(flags, packageName); try { IIntentSender target = ActivityManager.getService().getIntentSenderWithFeature( @@ -572,6 +622,7 @@ public final class PendingIntent implements Parcelable { String packageName = context.getPackageName(); String resolvedType = intent != null ? intent.resolveTypeIfNeeded( context.getContentResolver()) : null; + checkFlags(flags, packageName); try { intent.prepareToLeaveProcess(context); IIntentSender target = @@ -651,6 +702,7 @@ public final class PendingIntent implements Parcelable { String packageName = context.getPackageName(); String resolvedType = intent != null ? intent.resolveTypeIfNeeded( context.getContentResolver()) : null; + checkFlags(flags, packageName); try { intent.prepareToLeaveProcess(context); IIntentSender target = diff --git a/core/java/android/bluetooth/BluetoothCodecConfig.java b/core/java/android/bluetooth/BluetoothCodecConfig.java index e07bc0215a6b..a52fc891790c 100644 --- a/core/java/android/bluetooth/BluetoothCodecConfig.java +++ b/core/java/android/bluetooth/BluetoothCodecConfig.java @@ -51,19 +51,25 @@ public final class BluetoothCodecConfig implements Parcelable { @Retention(RetentionPolicy.SOURCE) public @interface SourceCodecType {} + @UnsupportedAppUsage public static final int SOURCE_CODEC_TYPE_SBC = 0; + @UnsupportedAppUsage public static final int SOURCE_CODEC_TYPE_AAC = 1; + @UnsupportedAppUsage public static final int SOURCE_CODEC_TYPE_APTX = 2; + @UnsupportedAppUsage public static final int SOURCE_CODEC_TYPE_APTX_HD = 3; + @UnsupportedAppUsage public static final int SOURCE_CODEC_TYPE_LDAC = 4; + @UnsupportedAppUsage public static final int SOURCE_CODEC_TYPE_MAX = 5; - + @UnsupportedAppUsage public static final int SOURCE_CODEC_TYPE_INVALID = 1000 * 1000; /** @hide */ @@ -75,10 +81,13 @@ public final class BluetoothCodecConfig implements Parcelable { @Retention(RetentionPolicy.SOURCE) public @interface CodecPriority {} + @UnsupportedAppUsage public static final int CODEC_PRIORITY_DISABLED = -1; + @UnsupportedAppUsage public static final int CODEC_PRIORITY_DEFAULT = 0; + @UnsupportedAppUsage public static final int CODEC_PRIORITY_HIGHEST = 1000 * 1000; @@ -95,18 +104,25 @@ public final class BluetoothCodecConfig implements Parcelable { @Retention(RetentionPolicy.SOURCE) public @interface SampleRate {} + @UnsupportedAppUsage public static final int SAMPLE_RATE_NONE = 0; + @UnsupportedAppUsage public static final int SAMPLE_RATE_44100 = 0x1 << 0; + @UnsupportedAppUsage public static final int SAMPLE_RATE_48000 = 0x1 << 1; + @UnsupportedAppUsage public static final int SAMPLE_RATE_88200 = 0x1 << 2; + @UnsupportedAppUsage public static final int SAMPLE_RATE_96000 = 0x1 << 3; + @UnsupportedAppUsage public static final int SAMPLE_RATE_176400 = 0x1 << 4; + @UnsupportedAppUsage public static final int SAMPLE_RATE_192000 = 0x1 << 5; @@ -120,12 +136,16 @@ public final class BluetoothCodecConfig implements Parcelable { @Retention(RetentionPolicy.SOURCE) public @interface BitsPerSample {} + @UnsupportedAppUsage public static final int BITS_PER_SAMPLE_NONE = 0; + @UnsupportedAppUsage public static final int BITS_PER_SAMPLE_16 = 0x1 << 0; + @UnsupportedAppUsage public static final int BITS_PER_SAMPLE_24 = 0x1 << 1; + @UnsupportedAppUsage public static final int BITS_PER_SAMPLE_32 = 0x1 << 2; @@ -138,10 +158,13 @@ public final class BluetoothCodecConfig implements Parcelable { @Retention(RetentionPolicy.SOURCE) public @interface ChannelMode {} + @UnsupportedAppUsage public static final int CHANNEL_MODE_NONE = 0; + @UnsupportedAppUsage public static final int CHANNEL_MODE_MONO = 0x1 << 0; + @UnsupportedAppUsage public static final int CHANNEL_MODE_STEREO = 0x1 << 1; private final @SourceCodecType int mCodecType; @@ -154,6 +177,7 @@ public final class BluetoothCodecConfig implements Parcelable { private final long mCodecSpecific3; private final long mCodecSpecific4; + @UnsupportedAppUsage public BluetoothCodecConfig(@SourceCodecType int codecType, @CodecPriority int codecPriority, @SampleRate int sampleRate, @BitsPerSample int bitsPerSample, @ChannelMode int channelMode, long codecSpecific1, @@ -170,6 +194,7 @@ public final class BluetoothCodecConfig implements Parcelable { mCodecSpecific4 = codecSpecific4; } + @UnsupportedAppUsage public BluetoothCodecConfig(@SourceCodecType int codecType) { mCodecType = codecType; mCodecPriority = BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT; @@ -391,6 +416,7 @@ public final class BluetoothCodecConfig implements Parcelable { * * @return the codec type */ + @UnsupportedAppUsage public @SourceCodecType int getCodecType() { return mCodecType; } @@ -411,6 +437,7 @@ public final class BluetoothCodecConfig implements Parcelable { * * @return the codec priority */ + @UnsupportedAppUsage public @CodecPriority int getCodecPriority() { return mCodecPriority; } @@ -441,6 +468,7 @@ public final class BluetoothCodecConfig implements Parcelable { * * @return the codec sample rate */ + @UnsupportedAppUsage public @SampleRate int getSampleRate() { return mSampleRate; } @@ -455,6 +483,7 @@ public final class BluetoothCodecConfig implements Parcelable { * * @return the codec bits per sample */ + @UnsupportedAppUsage public @BitsPerSample int getBitsPerSample() { return mBitsPerSample; } @@ -479,6 +508,7 @@ public final class BluetoothCodecConfig implements Parcelable { * * @return a codec specific value1. */ + @UnsupportedAppUsage public long getCodecSpecific1() { return mCodecSpecific1; } diff --git a/core/java/android/bluetooth/BluetoothCodecStatus.java b/core/java/android/bluetooth/BluetoothCodecStatus.java index 1e394b830d51..7b567b4098e7 100644 --- a/core/java/android/bluetooth/BluetoothCodecStatus.java +++ b/core/java/android/bluetooth/BluetoothCodecStatus.java @@ -17,6 +17,7 @@ package android.bluetooth; import android.annotation.Nullable; +import android.compat.annotation.UnsupportedAppUsage; import android.os.Parcel; import android.os.Parcelable; @@ -38,6 +39,7 @@ public final class BluetoothCodecStatus implements Parcelable { * This extra represents the current codec status of the A2DP * profile. */ + @UnsupportedAppUsage public static final String EXTRA_CODEC_STATUS = "android.bluetooth.extra.CODEC_STATUS"; @@ -196,6 +198,7 @@ public final class BluetoothCodecStatus implements Parcelable { * * @return the current codec configuration */ + @UnsupportedAppUsage public @Nullable BluetoothCodecConfig getCodecConfig() { return mCodecConfig; } @@ -205,6 +208,7 @@ public final class BluetoothCodecStatus implements Parcelable { * * @return an array with the codecs local capabilities */ + @UnsupportedAppUsage public @Nullable BluetoothCodecConfig[] getCodecsLocalCapabilities() { return mCodecsLocalCapabilities; } @@ -214,6 +218,7 @@ public final class BluetoothCodecStatus implements Parcelable { * * @return an array with the codecs selectable capabilities */ + @UnsupportedAppUsage public @Nullable BluetoothCodecConfig[] getCodecsSelectableCapabilities() { return mCodecsSelectableCapabilities; } diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 5452f92c929f..005648ffec36 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -351,6 +351,13 @@ public abstract class Context { * due to its foreground state such as an activity or foreground service, then this flag will * allow the bound app to get the same capabilities, as long as it has the required permissions * as well. + * + * If binding from a top app and its target SDK version is at or above + * {@link android.os.Build.VERSION_CODES#R}, the app needs to + * explicitly use BIND_INCLUDE_CAPABILITIES flag to pass all capabilities to the service so the + * other app can have while-use-use access such as location, camera, microphone from background. + * If binding from a top app and its target SDK version is below + * {@link android.os.Build.VERSION_CODES#R}, BIND_INCLUDE_CAPABILITIES is implicit. */ public static final int BIND_INCLUDE_CAPABILITIES = 0x000001000; @@ -3179,8 +3186,8 @@ public abstract class Context { * {@link #BIND_AUTO_CREATE}, {@link #BIND_DEBUG_UNBIND}, * {@link #BIND_NOT_FOREGROUND}, {@link #BIND_ABOVE_CLIENT}, * {@link #BIND_ALLOW_OOM_MANAGEMENT}, {@link #BIND_WAIVE_PRIORITY}. - * {@link #BIND_IMPORTANT}, or - * {@link #BIND_ADJUST_WITH_ACTIVITY}. + * {@link #BIND_IMPORTANT}, {@link #BIND_ADJUST_WITH_ACTIVITY}, + * {@link #BIND_NOT_PERCEPTIBLE}, or {@link #BIND_INCLUDE_CAPABILITIES}. * @return {@code true} if the system is in the process of bringing up a * service that your client has permission to bind to; {@code false} * if the system couldn't find the service or if your client doesn't @@ -3201,6 +3208,8 @@ public abstract class Context { * @see #BIND_WAIVE_PRIORITY * @see #BIND_IMPORTANT * @see #BIND_ADJUST_WITH_ACTIVITY + * @see #BIND_NOT_PERCEPTIBLE + * @see #BIND_INCLUDE_CAPABILITIES */ public abstract boolean bindService(@RequiresPermission Intent service, @NonNull ServiceConnection conn, @BindServiceFlags int flags); diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java index da56abf73516..a9f143987cd4 100644 --- a/core/java/android/content/pm/PackageInstaller.java +++ b/core/java/android/content/pm/PackageInstaller.java @@ -1878,6 +1878,7 @@ public class PackageInstaller { /** {@hide} */ @SystemApi + @TestApi public void setInstallAsInstantApp(boolean isInstantApp) { if (isInstantApp) { installFlags |= PackageManager.INSTALL_INSTANT_APP; diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java index 8f99edf61ece..dda1890b0757 100644 --- a/core/java/android/hardware/display/DisplayManager.java +++ b/core/java/android/hardware/display/DisplayManager.java @@ -941,6 +941,7 @@ public final class DisplayManager { */ String KEY_PEAK_REFRESH_RATE_DEFAULT = "peak_refresh_rate_default"; + // TODO(b/162536543): rename it once it is proved not harmful for users. /** * Key for controlling which packages are explicitly blocked from running at refresh rates * higher than 60hz. An app may be added to this list if they exhibit performance issues at diff --git a/core/java/android/hardware/soundtrigger/ConversionUtil.java b/core/java/android/hardware/soundtrigger/ConversionUtil.java index c4d123ca4382..06b5b6745bd1 100644 --- a/core/java/android/hardware/soundtrigger/ConversionUtil.java +++ b/core/java/android/hardware/soundtrigger/ConversionUtil.java @@ -32,10 +32,12 @@ import android.media.soundtrigger_middleware.RecognitionMode; import android.media.soundtrigger_middleware.SoundModel; import android.media.soundtrigger_middleware.SoundTriggerModuleDescriptor; import android.media.soundtrigger_middleware.SoundTriggerModuleProperties; +import android.os.ParcelFileDescriptor; import android.os.SharedMemory; import android.system.ErrnoException; import java.io.FileDescriptor; +import java.io.IOException; import java.nio.ByteBuffer; import java.util.Arrays; import java.util.UUID; @@ -109,7 +111,12 @@ class ConversionUtil { aidlModel.type = apiModel.getType(); aidlModel.uuid = api2aidlUuid(apiModel.getUuid()); aidlModel.vendorUuid = api2aidlUuid(apiModel.getVendorUuid()); - aidlModel.data = byteArrayToSharedMemory(apiModel.getData(), "SoundTrigger SoundModel"); + try { + aidlModel.data = ParcelFileDescriptor.dup( + byteArrayToSharedMemory(apiModel.getData(), "SoundTrigger SoundModel")); + } catch (IOException e) { + throw new RuntimeException(e); + } aidlModel.dataSize = apiModel.getData().length; return aidlModel; } diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java index b15109e67086..d80a7e794220 100644 --- a/core/java/android/permission/PermissionManager.java +++ b/core/java/android/permission/PermissionManager.java @@ -34,7 +34,6 @@ import android.content.Context; import android.content.pm.IPackageManager; import android.content.pm.PackageManager; import android.content.pm.permission.SplitPermissionInfoParcelable; -import android.os.Binder; import android.os.Process; import android.os.RemoteException; import android.os.ServiceManager; @@ -544,15 +543,10 @@ public final class PermissionManager { + permission); return PackageManager.PERMISSION_DENIED; } - // Clear Binder.callingUid in case this is called inside the system server. See - // more extensive comment in checkPackageNamePermissionUncached - long token = Binder.clearCallingIdentity(); try { return am.checkPermission(permission, pid, uid); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); - } finally { - Binder.restoreCallingIdentity(token); } } @@ -685,20 +679,11 @@ public final class PermissionManager { /* @hide */ private static int checkPackageNamePermissionUncached( String permName, String pkgName, @UserIdInt int userId) { - // Makeing the binder call "checkPermission" usually sets Binder.callingUid to the calling - // processes UID. Hence clearing the calling UID is superflous. - // If the call is inside the system server though "checkPermission" is not a binder all, it - // is only a method call. Hence Binder.callingUid might still be set to the app that called - // the system server. This can lead to problems as not every app can check the same - // permissions the system server can check. - long token = Binder.clearCallingIdentity(); try { return ActivityThread.getPermissionManager().checkPermission( permName, pkgName, userId); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); - } finally { - Binder.restoreCallingIdentity(token); } } diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 8a5963ed02eb..a7055ec0f050 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -7954,6 +7954,12 @@ public final class Settings { public static final String UI_NIGHT_MODE_OVERRIDE_ON = "ui_night_mode_override_on"; /** + * The last computed night mode bool the last time the phone was on + * @hide + */ + public static final String UI_NIGHT_MODE_LAST_COMPUTED = "ui_night_mode_last_computed"; + + /** * The current night mode that has been overridden to turn off by the system. Owned * and controlled by UiModeManagerService. Constants are as per * UiModeManager. @@ -9078,6 +9084,22 @@ public final class Settings { }; /** + * How long Assistant handles have enabled in milliseconds. + * + * @hide + */ + public static final String ASSIST_HANDLES_LEARNING_TIME_ELAPSED_MILLIS = + "reminder_exp_learning_time_elapsed"; + + /** + * How many times the Assistant has been triggered using the touch gesture. + * + * @hide + */ + public static final String ASSIST_HANDLES_LEARNING_EVENT_COUNT = + "reminder_exp_learning_event_count"; + + /** * These entries are considered common between the personal and the managed profile, * since the managed profile doesn't get to change them. */ diff --git a/core/java/android/util/IntArray.java b/core/java/android/util/IntArray.java index 5a74ec0e52c0..b77265b0ebf6 100644 --- a/core/java/android/util/IntArray.java +++ b/core/java/android/util/IntArray.java @@ -144,6 +144,17 @@ public class IntArray implements Cloneable { } /** + * Adds the values in the specified array to this array. + */ + public void addAll(int[] values) { + final int count = values.length; + ensureCapacity(count); + + System.arraycopy(values, 0, mValues, mSize, count); + mSize += count; + } + + /** * Ensures capacity to append at least <code>count</code> values. */ private void ensureCapacity(int count) { diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java index d55c25fc1a4f..c698b926e233 100644 --- a/core/java/android/view/SurfaceControl.java +++ b/core/java/android/view/SurfaceControl.java @@ -316,11 +316,19 @@ public final class SurfaceControl implements Parcelable { public static final int HIDDEN = 0x00000004; /** - * Surface creation flag: The surface contains secure content, special - * measures will be taken to disallow the surface's content to be copied - * from another process. In particular, screenshots and VNC servers will - * be disabled, but other measures can take place, for instance the - * surface might not be hardware accelerated. + * Surface creation flag: Skip this layer and its children when taking a screenshot. This + * also includes mirroring and screen recording, so the layers with flag SKIP_SCREENSHOT + * will not be included on non primary displays. + * @hide + */ + public static final int SKIP_SCREENSHOT = 0x00000040; + + /** + * Surface creation flag: Special measures will be taken to disallow the surface's content to + * be copied. In particular, screenshots and secondary, non-secure displays will render black + * content instead of the surface content. + * + * @see #createDisplay(String, boolean) * @hide */ public static final int SECURE = 0x00000080; @@ -482,15 +490,6 @@ public final class SurfaceControl implements Parcelable { public static final int POWER_MODE_ON_SUSPEND = 4; /** - * A value for windowType used to indicate that the window should be omitted from screenshots - * and display mirroring. A temporary workaround until we express such things with - * the hierarchy. - * TODO: b/64227542 - * @hide - */ - public static final int WINDOW_TYPE_DONT_SCREENSHOT = 441731; - - /** * internal representation of how to interpret pixel value, used only to convert to ColorSpace. */ private static final int INTERNAL_DATASPACE_SRGB = 142671872; @@ -3287,6 +3286,22 @@ public final class SurfaceControl implements Parcelable { return this; } + /** + * Adds or removes the flag SKIP_SCREENSHOT of the surface. Setting the flag is equivalent + * to creating the Surface with the {@link #SKIP_SCREENSHOT} flag. + * + * @hide + */ + public Transaction setSkipScreenshot(SurfaceControl sc, boolean skipScrenshot) { + checkPreconditions(sc); + if (skipScrenshot) { + nativeSetFlags(mNativeObject, sc.mNativeObject, SKIP_SCREENSHOT, SKIP_SCREENSHOT); + } else { + nativeSetFlags(mNativeObject, sc.mNativeObject, 0, SKIP_SCREENSHOT); + } + return this; + } + /** * Merge the other transaction into this transaction, clearing the * other transaction as if it had been applied. diff --git a/core/java/com/android/internal/jank/FrameTracker.java b/core/java/com/android/internal/jank/FrameTracker.java index dd1978ebb026..85dc2aea4b1c 100644 --- a/core/java/com/android/internal/jank/FrameTracker.java +++ b/core/java/com/android/internal/jank/FrameTracker.java @@ -24,11 +24,11 @@ import android.util.Log; import android.view.FrameMetrics; import android.view.ThreadedRenderer; -import com.android.internal.annotations.VisibleForTesting; import com.android.internal.jank.InteractionJankMonitor.Session; import com.android.internal.util.FrameworkStatsLog; /** + * A class that allows the app to get the frame metrics from HardwareRendererObserver. * @hide */ public class FrameTracker implements HardwareRendererObserver.OnFrameMetricsAvailableListener { @@ -45,28 +45,21 @@ public class FrameTracker implements HardwareRendererObserver.OnFrameMetricsAvai private long mBeginTime = UNKNOWN_TIMESTAMP; private long mEndTime = UNKNOWN_TIMESTAMP; private boolean mShouldTriggerTrace; + private boolean mSessionEnd; private int mTotalFramesCount = 0; private int mMissedFramesCount = 0; private long mMaxFrameTimeNanos = 0; private Session mSession; - public FrameTracker(@NonNull Session session, - @NonNull Handler handler, @NonNull ThreadedRenderer renderer) { - mSession = session; - mRendererWrapper = new ThreadedRendererWrapper(renderer); - mMetricsWrapper = new FrameMetricsWrapper(); - mObserver = new HardwareRendererObserver(this, mMetricsWrapper.getTiming(), handler); - } - /** - * This constructor is only for unit tests. + * Constructor of FrameTracker. * @param session a trace session. - * @param renderer a test double for ThreadedRenderer - * @param metrics a test double for FrameMetrics + * @param handler a handler for handling callbacks. + * @param renderer a ThreadedRendererWrapper instance. + * @param metrics a FrameMetricsWrapper instance. */ - @VisibleForTesting - public FrameTracker(@NonNull Session session, Handler handler, + public FrameTracker(@NonNull Session session, @NonNull Handler handler, @NonNull ThreadedRendererWrapper renderer, @NonNull FrameMetricsWrapper metrics) { mSession = session; mRendererWrapper = renderer; @@ -77,15 +70,11 @@ public class FrameTracker implements HardwareRendererObserver.OnFrameMetricsAvai /** * Begin a trace session of the CUJ. */ - public void begin() { + public synchronized void begin() { long timestamp = System.nanoTime(); if (DEBUG) { Log.d(TAG, "begin: time(ns)=" + timestamp + ", begin(ns)=" + mBeginTime - + ", end(ns)=" + mEndTime + ", session=" + mSession); - } - if (mBeginTime != UNKNOWN_TIMESTAMP && mEndTime == UNKNOWN_TIMESTAMP) { - // We have an ongoing tracing already, skip subsequent calls. - return; + + ", end(ns)=" + mEndTime + ", session=" + mSession.getName()); } mBeginTime = timestamp; mEndTime = UNKNOWN_TIMESTAMP; @@ -96,32 +85,48 @@ public class FrameTracker implements HardwareRendererObserver.OnFrameMetricsAvai /** * End the trace session of the CUJ. */ - public void end() { + public synchronized void end() { long timestamp = System.nanoTime(); if (DEBUG) { Log.d(TAG, "end: time(ns)=" + timestamp + ", begin(ns)=" + mBeginTime - + ", end(ns)=" + mEndTime + ", session=" + mSession); - } - if (mBeginTime == UNKNOWN_TIMESTAMP || mEndTime != UNKNOWN_TIMESTAMP) { - // We haven't started a trace yet. - return; + + ", end(ns)=" + mEndTime + ", session=" + mSession.getName()); } mEndTime = timestamp; Trace.endAsyncSection(mSession.getName(), (int) mBeginTime); + // We don't remove observer here, + // will remove it when all the frame metrics in this duration are called back. + // See onFrameMetricsAvailable for the logic of removing the observer. + } + + /** + * Cancel the trace session of the CUJ. + */ + public synchronized void cancel() { + if (mBeginTime == UNKNOWN_TIMESTAMP || mEndTime != UNKNOWN_TIMESTAMP) return; + if (DEBUG) { + Log.d(TAG, "cancel: time(ns)=" + System.nanoTime() + ", begin(ns)=" + mBeginTime + + ", end(ns)=" + mEndTime + ", session=" + mSession.getName()); + } + Trace.endAsyncSection(mSession.getName(), (int) mBeginTime); + mRendererWrapper.removeObserver(mObserver); + mBeginTime = UNKNOWN_TIMESTAMP; + mEndTime = UNKNOWN_TIMESTAMP; + mShouldTriggerTrace = false; } @Override - public void onFrameMetricsAvailable(int dropCountSinceLastInvocation) { + public synchronized void onFrameMetricsAvailable(int dropCountSinceLastInvocation) { // Since this callback might come a little bit late after the end() call. // We should keep tracking the begin / end timestamp. // Then compare with vsync timestamp to check if the frame is in the duration of the CUJ. - if (mBeginTime == UNKNOWN_TIMESTAMP) return; // We haven't started tracing yet. long vsyncTimestamp = mMetricsWrapper.getMetric(FrameMetrics.VSYNC_TIMESTAMP); - if (vsyncTimestamp < mBeginTime) return; // The tracing has been started. + // Discard the frame metrics which is not in the trace session. + if (vsyncTimestamp < mBeginTime) return; - // If the end time has not been set, we are still in the tracing. - if (mEndTime != UNKNOWN_TIMESTAMP && vsyncTimestamp > mEndTime) { + // We stop getting callback when the vsync is later than the end calls. + if (mEndTime != UNKNOWN_TIMESTAMP && vsyncTimestamp > mEndTime && !mSessionEnd) { + mSessionEnd = true; // The tracing has been ended, remove the observer, see if need to trigger perfetto. mRendererWrapper.removeObserver(mObserver); @@ -170,9 +175,8 @@ public class FrameTracker implements HardwareRendererObserver.OnFrameMetricsAvai /** * Trigger the prefetto daemon. */ - @VisibleForTesting public void triggerPerfetto() { - InteractionJankMonitor.trigger(); + InteractionJankMonitor.getInstance().trigger(); } /** diff --git a/core/java/com/android/internal/jank/InteractionJankMonitor.java b/core/java/com/android/internal/jank/InteractionJankMonitor.java index 5a0cbf9e93e4..19dc2ed6daea 100644 --- a/core/java/com/android/internal/jank/InteractionJankMonitor.java +++ b/core/java/com/android/internal/jank/InteractionJankMonitor.java @@ -21,15 +21,17 @@ import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_IN import android.annotation.IntDef; import android.annotation.NonNull; import android.os.HandlerThread; -import android.view.ThreadedRenderer; +import android.util.Log; +import android.util.SparseArray; import android.view.View; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.jank.FrameTracker.FrameMetricsWrapper; +import com.android.internal.jank.FrameTracker.ThreadedRendererWrapper; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; -import java.util.HashMap; -import java.util.Map; +import java.util.concurrent.TimeUnit; /** * This class let users to begin and end the always on tracing mechanism. @@ -38,11 +40,17 @@ import java.util.Map; public class InteractionJankMonitor { private static final String TAG = InteractionJankMonitor.class.getSimpleName(); private static final boolean DEBUG = false; - private static final Object LOCK = new Object(); + private static final String DEFAULT_WORKER_NAME = TAG + "-Worker"; + private static final long DEFAULT_TIMEOUT_MS = TimeUnit.SECONDS.toMillis(5L); // Every value must have a corresponding entry in CUJ_STATSD_INTERACTION_TYPE. - public static final int CUJ_NOTIFICATION_SHADE_MOTION = 0; - public static final int CUJ_NOTIFICATION_SHADE_GESTURE = 1; + public static final int CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE = 1; + public static final int CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE_LOCK = 0; + public static final int CUJ_NOTIFICATION_SHADE_SCROLL_FLING = 0; + public static final int CUJ_NOTIFICATION_SHADE_ROW_EXPAND = 0; + public static final int CUJ_NOTIFICATION_SHADE_ROW_SWIPE = 0; + public static final int CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE = 0; + public static final int CUJ_NOTIFICATION_SHADE_QS_SCROLL_SWIPE = 0; private static final int NO_STATSD_LOGGING = -1; @@ -53,141 +61,255 @@ public class InteractionJankMonitor { UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__NOTIFICATION_SHADE_SWIPE, }; - private static ThreadedRenderer sRenderer; - private static Map<String, FrameTracker> sRunningTracker; - private static HandlerThread sWorker; - private static boolean sInitialized; + private static volatile InteractionJankMonitor sInstance; + + private ThreadedRendererWrapper mRenderer; + private FrameMetricsWrapper mMetrics; + private SparseArray<FrameTracker> mRunningTrackers; + private SparseArray<Runnable> mTimeoutActions; + private HandlerThread mWorker; + private boolean mInitialized; /** @hide */ @IntDef({ - CUJ_NOTIFICATION_SHADE_MOTION, - CUJ_NOTIFICATION_SHADE_GESTURE + CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE, + CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE_LOCK, + CUJ_NOTIFICATION_SHADE_SCROLL_FLING, + CUJ_NOTIFICATION_SHADE_ROW_EXPAND, + CUJ_NOTIFICATION_SHADE_ROW_SWIPE, + CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE, + CUJ_NOTIFICATION_SHADE_QS_SCROLL_SWIPE }) @Retention(RetentionPolicy.SOURCE) public @interface CujType {} /** - * @param view Any view in the view tree to get context and ThreadedRenderer. + * Get the singleton of InteractionJankMonitor. + * @return instance of InteractionJankMonitor */ - public static void init(@NonNull View view) { - init(view, null, null, null); + public static InteractionJankMonitor getInstance() { + // Use DCL here since this method might be invoked very often. + if (sInstance == null) { + synchronized (InteractionJankMonitor.class) { + if (sInstance == null) { + sInstance = new InteractionJankMonitor(new HandlerThread(DEFAULT_WORKER_NAME)); + } + } + } + return sInstance; } /** - * Should be only invoked internally or from unit tests. + * This constructor should be only public to tests. + * @param worker the worker thread for the callbacks */ @VisibleForTesting - public static void init(@NonNull View view, @NonNull ThreadedRenderer renderer, - @NonNull Map<String, FrameTracker> map, @NonNull HandlerThread worker) { + public InteractionJankMonitor(@NonNull HandlerThread worker) { + mRunningTrackers = new SparseArray<>(); + mTimeoutActions = new SparseArray<>(); + mWorker = worker; + } + + /** + * Init InteractionJankMonitor for later instrumentation. + * @param view Any view in the view tree to get context and ThreadedRenderer. + * @return boolean true if the instance has been initialized successfully. + */ + public boolean init(@NonNull View view) { //TODO (163505250): This should be no-op if not in droid food rom. - synchronized (LOCK) { - if (!sInitialized) { - if (!view.isAttachedToWindow()) { - throw new IllegalStateException("View is not attached!"); + if (!mInitialized) { + synchronized (this) { + if (!mInitialized) { + if (!view.isAttachedToWindow()) { + Log.d(TAG, "Expect an attached view!", new Throwable()); + return false; + } + mRenderer = new ThreadedRendererWrapper(view.getThreadedRenderer()); + mMetrics = new FrameMetricsWrapper(); + mWorker.start(); + mInitialized = true; } - sRenderer = renderer == null ? view.getThreadedRenderer() : renderer; - sRunningTracker = map == null ? new HashMap<>() : map; - sWorker = worker == null ? new HandlerThread("Aot-Worker") : worker; - sWorker.start(); - sInitialized = true; } } + return true; } /** - * Must invoke init() before invoking this method. + * Create a {@link FrameTracker} instance. + * @param session the session associates with this tracker + * @return instance of the FrameTracker */ - public static void begin(@NonNull @CujType int cujType) { - begin(cujType, null); + @VisibleForTesting + public FrameTracker createFrameTracker(Session session) { + synchronized (this) { + if (!mInitialized) return null; + return new FrameTracker(session, mWorker.getThreadHandler(), mRenderer, mMetrics); + } } /** - * Should be only invoked internally or from unit tests. + * Begin a trace session, must invoke {@link #init(View)} before invoking this method. + * @param cujType the specific {@link InteractionJankMonitor.CujType}. + * @return boolean true if the tracker is started successfully, false otherwise. */ - @VisibleForTesting - public static void begin(@NonNull @CujType int cujType, FrameTracker tracker) { + public boolean begin(@CujType int cujType) { //TODO (163505250): This should be no-op if not in droid food rom. - //TODO (163510843): Remove synchronized, add @UiThread if only invoked from ui threads. - synchronized (LOCK) { - checkInitStateLocked(); - Session session = new Session(cujType); - FrameTracker currentTracker = getTracker(session.getName()); - if (currentTracker != null) return; - if (tracker == null) { - tracker = new FrameTracker(session, sWorker.getThreadHandler(), sRenderer); + synchronized (this) { + return begin(cujType, 0L /* timeout */); + } + } + + /** + * Begin a trace session, must invoke {@link #init(View)} before invoking this method. + * @param cujType the specific {@link InteractionJankMonitor.CujType}. + * @param timeout the elapsed time in ms until firing the timeout action. + * @return boolean true if the tracker is started successfully, false otherwise. + */ + public boolean begin(@CujType int cujType, long timeout) { + //TODO (163505250): This should be no-op if not in droid food rom. + synchronized (this) { + if (!mInitialized) { + Log.d(TAG, "Not initialized!", new Throwable()); + return false; } - sRunningTracker.put(session.getName(), tracker); + Session session = new Session(cujType); + FrameTracker tracker = getTracker(session); + // Skip subsequent calls if we already have an ongoing tracing. + if (tracker != null) return false; + + // begin a new trace session. + tracker = createFrameTracker(session); + mRunningTrackers.put(cujType, tracker); tracker.begin(); + + // Cancel the trace if we don't get an end() call in specified duration. + timeout = timeout > 0L ? timeout : DEFAULT_TIMEOUT_MS; + Runnable timeoutAction = () -> cancel(cujType); + mTimeoutActions.put(cujType, timeoutAction); + mWorker.getThreadHandler().postDelayed(timeoutAction, timeout); + return true; } } /** - * Must invoke init() before invoking this method. + * End a trace session, must invoke {@link #init(View)} before invoking this method. + * @param cujType the specific {@link InteractionJankMonitor.CujType}. + * @return boolean true if the tracker is ended successfully, false otherwise. */ - public static void end(@NonNull @CujType int cujType) { + public boolean end(@CujType int cujType) { //TODO (163505250): This should be no-op if not in droid food rom. - //TODO (163510843): Remove synchronized, add @UiThread if only invoked from ui threads. - synchronized (LOCK) { - checkInitStateLocked(); + synchronized (this) { + if (!mInitialized) { + Log.d(TAG, "Not initialized!", new Throwable()); + return false; + } + // remove the timeout action first. + Runnable timeout = mTimeoutActions.get(cujType); + if (timeout != null) { + mWorker.getThreadHandler().removeCallbacks(timeout); + mTimeoutActions.remove(cujType); + } + Session session = new Session(cujType); - FrameTracker tracker = getTracker(session.getName()); - if (tracker != null) { - tracker.end(); - sRunningTracker.remove(session.getName()); + FrameTracker tracker = getTracker(session); + // Skip this call since we haven't started a trace yet. + if (tracker == null) return false; + tracker.end(); + mRunningTrackers.remove(session.getCuj()); + return true; + } + } + + /** + * Cancel the trace session, must invoke {@link #init(View)} before invoking this method. + * @return boolean true if the tracker is cancelled successfully, false otherwise. + */ + public boolean cancel(@CujType int cujType) { + //TODO (163505250): This should be no-op if not in droid food rom. + synchronized (this) { + if (!mInitialized) { + Log.d(TAG, "Not initialized!", new Throwable()); + return false; } + // remove the timeout action first. + Runnable timeout = mTimeoutActions.get(cujType); + if (timeout != null) { + mWorker.getThreadHandler().removeCallbacks(timeout); + mTimeoutActions.remove(cujType); + } + + Session session = new Session(cujType); + FrameTracker tracker = getTracker(session); + // Skip this call since we haven't started a trace yet. + if (tracker == null) return false; + tracker.cancel(); + mRunningTrackers.remove(session.getCuj()); + return true; } } - private static void checkInitStateLocked() { - if (!sInitialized) { - throw new IllegalStateException("InteractionJankMonitor not initialized!"); + private void destroy() { + synchronized (this) { + int trackers = mRunningTrackers.size(); + for (int i = 0; i < trackers; i++) { + mRunningTrackers.valueAt(i).cancel(); + } + mRunningTrackers = null; + mTimeoutActions.clear(); + mTimeoutActions = null; + mWorker.quit(); + mWorker = null; } } /** - * Should be only invoked from unit tests. + * Abandon current instance. */ @VisibleForTesting - public static void reset() { - sInitialized = false; - sRenderer = null; - sRunningTracker = null; - if (sWorker != null) { - sWorker.quit(); - sWorker = null; + public static void abandon() { + if (sInstance == null) return; + synchronized (InteractionJankMonitor.class) { + if (sInstance == null) return; + sInstance.destroy(); + sInstance = null; } } - private static FrameTracker getTracker(String sessionName) { - synchronized (LOCK) { - return sRunningTracker.get(sessionName); + private FrameTracker getTracker(Session session) { + synchronized (this) { + if (!mInitialized) return null; + return mRunningTrackers.get(session.getCuj()); } } /** * Trigger the perfetto daemon to collect and upload data. */ - public static void trigger() { - sWorker.getThreadHandler().post( - () -> PerfettoTrigger.trigger(PerfettoTrigger.TRIGGER_TYPE_JANK)); + @VisibleForTesting + public void trigger() { + synchronized (this) { + if (!mInitialized) return; + mWorker.getThreadHandler().post( + () -> PerfettoTrigger.trigger(PerfettoTrigger.TRIGGER_TYPE_JANK)); + } } /** * A class to represent a session. */ public static class Session { - private @CujType int mId; + private @CujType int mCujType; - public Session(@CujType int session) { - mId = session; + public Session(@CujType int cujType) { + mCujType = cujType; } - public int getId() { - return mId; + public int getCuj() { + return mCujType; } public int getStatsdInteractionType() { - return CUJ_TO_STATSD_INTERACTION_TYPE[mId]; + return CUJ_TO_STATSD_INTERACTION_TYPE[mCujType]; } /** Describes whether the measurement from this session should be written to statsd. */ @@ -196,7 +318,7 @@ public class InteractionJankMonitor { } public String getName() { - return "CujType<" + mId + ">"; + return "Cuj<" + getCuj() + ">"; } } diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index e58990eff2c8..7516a879211f 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -6613,22 +6613,29 @@ public class BatteryStatsImpl extends BatteryStats { * the power consumption to the calling app. */ public void noteBinderCallStats(int workSourceUid, long incrementalCallCount, - Collection<BinderCallsStats.CallStat> callStats, int[] binderThreadNativeTids) { - noteBinderCallStats(workSourceUid, incrementalCallCount, callStats, binderThreadNativeTids, + Collection<BinderCallsStats.CallStat> callStats) { + noteBinderCallStats(workSourceUid, incrementalCallCount, callStats, mClocks.elapsedRealtime(), mClocks.uptimeMillis()); } public void noteBinderCallStats(int workSourceUid, long incrementalCallCount, - Collection<BinderCallsStats.CallStat> callStats, int[] binderThreadNativeTids, + Collection<BinderCallsStats.CallStat> callStats, long elapsedRealtimeMs, long uptimeMs) { synchronized (this) { getUidStatsLocked(workSourceUid, elapsedRealtimeMs, uptimeMs) .noteBinderCallStatsLocked(incrementalCallCount, callStats); - mSystemServerCpuThreadReader.setBinderThreadNativeTids(binderThreadNativeTids); } } /** + * Takes note of native IDs of threads taking incoming binder calls. The CPU time + * of these threads is attributed to the apps making those binder calls. + */ + public void noteBinderThreadNativeIds(int[] binderThreadNativeTids) { + mSystemServerCpuThreadReader.setBinderThreadNativeTids(binderThreadNativeTids); + } + + /** * Estimates the proportion of system server CPU activity handling incoming binder calls * that can be attributed to each app */ diff --git a/core/java/com/android/internal/os/BinderCallsStats.java b/core/java/com/android/internal/os/BinderCallsStats.java index f5bef0b006f5..70b1ad49d8b8 100644 --- a/core/java/com/android/internal/os/BinderCallsStats.java +++ b/core/java/com/android/internal/os/BinderCallsStats.java @@ -119,8 +119,8 @@ public class BinderCallsStats implements BinderInternal.Observer { if (uidEntry != null) { ArrayMap<CallStatKey, CallStat> callStats = uidEntry.mCallStats; mCallStatsObserver.noteCallStats(uidEntry.workSourceUid, - uidEntry.incrementalCallCount, callStats.values(), - mNativeTids.toArray()); + uidEntry.incrementalCallCount, callStats.values() + ); uidEntry.incrementalCallCount = 0; for (int j = callStats.size() - 1; j >= 0; j--) { callStats.valueAt(j).incrementalCallCount = 0; @@ -168,6 +168,7 @@ public class BinderCallsStats implements BinderInternal.Observer { public void setCallStatsObserver( BinderInternal.CallStatsObserver callStatsObserver) { mCallStatsObserver = callStatsObserver; + noteBinderThreadNativeIds(); noteCallsStatsDelayed(); } @@ -182,13 +183,13 @@ public class BinderCallsStats implements BinderInternal.Observer { @Override @Nullable public CallSession callStarted(Binder binder, int code, int workSourceUid) { + noteNativeThreadId(); + if (!mRecordingAllTransactionsForUid && (mDeviceState == null || mDeviceState.isCharging())) { return null; } - noteNativeThreadId(); - final CallSession s = obtainCallSession(); s.binderClass = binder.getClass(); s.transactionCode = code; @@ -359,6 +360,16 @@ public class BinderCallsStats implements BinderInternal.Observer { mNativeTids = copyOnWriteArray; } } + + noteBinderThreadNativeIds(); + } + + private void noteBinderThreadNativeIds() { + if (mCallStatsObserver == null) { + return; + } + + mCallStatsObserver.noteBinderThreadNativeIds(getNativeTids()); } /** diff --git a/core/java/com/android/internal/os/BinderInternal.java b/core/java/com/android/internal/os/BinderInternal.java index 2645b8e84cf1..c14d8d805d29 100644 --- a/core/java/com/android/internal/os/BinderInternal.java +++ b/core/java/com/android/internal/os/BinderInternal.java @@ -143,8 +143,12 @@ public class BinderInternal { * Notes incoming binder call stats associated with this work source UID. */ void noteCallStats(int workSourceUid, long incrementalCallCount, - Collection<BinderCallsStats.CallStat> callStats, - int[] binderThreadNativeTids); + Collection<BinderCallsStats.CallStat> callStats); + + /** + * Notes the native IDs of threads taking incoming binder calls. + */ + void noteBinderThreadNativeIds(int[] binderThreadNativeTids); } /** diff --git a/core/java/com/android/internal/os/SystemServerCpuThreadReader.java b/core/java/com/android/internal/os/SystemServerCpuThreadReader.java index 1cdd42c7403e..3aa2390375ec 100644 --- a/core/java/com/android/internal/os/SystemServerCpuThreadReader.java +++ b/core/java/com/android/internal/os/SystemServerCpuThreadReader.java @@ -31,7 +31,7 @@ import java.util.Arrays; */ public class SystemServerCpuThreadReader { private KernelCpuThreadReader mKernelCpuThreadReader; - private int[] mBinderThreadNativeTids; + private int[] mBinderThreadNativeTids = new int[0]; // Sorted private int[] mThreadCpuTimesUs; private int[] mBinderThreadCpuTimesUs; @@ -75,7 +75,8 @@ public class SystemServerCpuThreadReader { } public void setBinderThreadNativeTids(int[] nativeTids) { - mBinderThreadNativeTids = nativeTids; + mBinderThreadNativeTids = nativeTids.clone(); + Arrays.sort(mBinderThreadNativeTids); } /** @@ -107,7 +108,8 @@ public class SystemServerCpuThreadReader { int threadCpuUsagesSize = threadCpuUsages.size(); for (int j = 0; j < threadCpuUsagesSize; j++) { KernelCpuThreadReader.ThreadCpuUsage tcu = threadCpuUsages.get(j); - boolean isBinderThread = isBinderThread(tcu.threadId); + boolean isBinderThread = + Arrays.binarySearch(mBinderThreadNativeTids, tcu.threadId) >= 0; final int len = Math.min(tcu.usageTimesMillis.length, mThreadCpuTimesUs.length); for (int k = 0; k < len; k++) { @@ -138,14 +140,4 @@ public class SystemServerCpuThreadReader { return mDeltaCpuThreadTimes; } - private boolean isBinderThread(int threadId) { - if (mBinderThreadNativeTids != null) { - for (int i = 0; i < mBinderThreadNativeTids.length; i++) { - if (threadId == mBinderThreadNativeTids[i]) { - return true; - } - } - } - return false; - } } diff --git a/core/java/com/android/internal/protolog/ProtoLogGroup.java b/core/java/com/android/internal/protolog/ProtoLogGroup.java index 9f7436a13bdc..9874c6aabf04 100644 --- a/core/java/com/android/internal/protolog/ProtoLogGroup.java +++ b/core/java/com/android/internal/protolog/ProtoLogGroup.java @@ -72,7 +72,9 @@ public enum ProtoLogGroup implements IProtoLogGroup { Consts.TAG_WM), WM_DEBUG_WINDOW_ORGANIZER(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false, Consts.TAG_WM), - TEST_GROUP(true, true, false, "WindowManagetProtoLogTest"); + WM_DEBUG_SYNC_ENGINE(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false, + Consts.TAG_WM), + TEST_GROUP(true, true, false, "WindowManagerProtoLogTest"); private final boolean mEnabled; private volatile boolean mLogToProto; diff --git a/core/proto/android/providers/settings/secure.proto b/core/proto/android/providers/settings/secure.proto index 5f590be72f52..e1a980caa635 100644 --- a/core/proto/android/providers/settings/secure.proto +++ b/core/proto/android/providers/settings/secure.proto @@ -120,6 +120,14 @@ message SecureSettingsProto { } optional Assist assist = 7; + message AssistHandles { + option (android.msg_privacy).dest = DEST_EXPLICIT; + + optional SettingProto learning_time_elapsed_millis = 1 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto learning_event_count = 2 [ (android.privacy).dest = DEST_AUTOMATIC ]; + } + optional AssistHandles assist_handles = 86; + message Autofill { option (android.msg_privacy).dest = DEST_EXPLICIT; @@ -617,5 +625,5 @@ message SecureSettingsProto { // Please insert fields in alphabetical order and group them into messages // if possible (to avoid reaching the method limit). - // Next tag = 86; + // Next tag = 87; } diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 03975daf1f76..25a9bbd8b445 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -4374,7 +4374,7 @@ </string> <!-- Dialog title for dialog shown when the multiple accessibility shortcut is activated, and we want to confirm that the user understands what's going to happen. [CHAR LIMIT=none] --> - <string name="accessibility_shortcut_multiple_service_warning_title">Turn on accessibility features?</string> + <string name="accessibility_shortcut_multiple_service_warning_title">Turn on shortcut for accessibility features?</string> <!-- Message shown in dialog when user is in the process of enabling the multiple accessibility service via the volume buttons shortcut for the first time. [CHAR LIMIT=none] --> <string name="accessibility_shortcut_multiple_service_warning">Holding down both volume keys for a few seconds turns on accessibility features. This may change how your device works.\n\nCurrent features:\n<xliff:g id="service" example="TalkBack">%1$s</xliff:g>\nYou can change selected features in Settings > Accessibility.</string> @@ -4383,7 +4383,7 @@ <string name="accessibility_shortcut_multiple_service_list">\t• <xliff:g id="service" example="TalkBack">%1$s</xliff:g>\n</string> <!-- Dialog title for dialog shown when this accessibility shortcut is activated, and we want to confirm that the user understands what's going to happen. [CHAR LIMIT=none] --> - <string name="accessibility_shortcut_single_service_warning_title">Turn on <xliff:g id="service" example="TalkBack">%1$s</xliff:g>?</string> + <string name="accessibility_shortcut_single_service_warning_title">Turn on <xliff:g id="service" example="TalkBack">%1$s</xliff:g> shortcut?</string> <!-- Message shown in dialog when user is in the process of enabling this accessibility service via the volume buttons shortcut for the first time. [CHAR LIMIT=none] --> <string name="accessibility_shortcut_single_service_warning">Holding down both volume keys for a few seconds turns on <xliff:g id="service" example="TalkBack">%1$s</xliff:g>, an accessibility feature. This may change how your device works.\n\nYou can change this shortcut to another feature in Settings > Accessibility.</string> diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml index 6ae6faa5446e..71cb2acde94e 100644 --- a/core/tests/coretests/AndroidManifest.xml +++ b/core/tests/coretests/AndroidManifest.xml @@ -228,15 +228,6 @@ </intent-filter> </activity> - <activity android:name="android.widget.focus.ListOfButtons" - android:label="ListOfButtons" - android:exported="true"> - <intent-filter> - <action android:name="android.intent.action.MAIN" /> - <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" /> - </intent-filter> - </activity> - <activity android:name="android.widget.focus.LinearLayoutGrid" android:label="LinearLayoutGrid" android:exported="true"> diff --git a/core/tests/coretests/src/android/widget/focus/ListOfEditTexts.java b/core/tests/coretests/src/android/widget/focus/ListOfEditTexts.java deleted file mode 100644 index 936c9999e7f8..000000000000 --- a/core/tests/coretests/src/android/widget/focus/ListOfEditTexts.java +++ /dev/null @@ -1,130 +0,0 @@ -/* - * Copyright (C) 2007 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.widget.focus; - -import android.app.Activity; -import android.content.Context; -import android.os.Bundle; -import android.view.View; -import android.view.ViewGroup; -import android.widget.ArrayAdapter; -import android.widget.Button; -import android.widget.EditText; -import android.widget.LinearLayout; -import android.widget.ListView; - -import com.google.android.collect.Lists; - -import java.util.List; - -public class ListOfEditTexts extends Activity { - - private int mLinesPerEditText = 12; - - private ListView mListView; - private LinearLayout mLinearLayout; - - public ListView getListView() { - return mListView; - } - - @Override - protected void onCreate(Bundle icicle) { - super.onCreate(icicle); - - // create linear layout - mLinearLayout = new LinearLayout(this); - mLinearLayout.setOrientation(LinearLayout.VERTICAL); - mLinearLayout.setLayoutParams(new ViewGroup.LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.MATCH_PARENT)); - - // add a button above - Button buttonAbove = new Button(this); - buttonAbove.setLayoutParams( - new LinearLayout.LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.WRAP_CONTENT)); - buttonAbove.setText("button above list"); - mLinearLayout.addView(buttonAbove); - - // add a list view to it - mListView = new ListView(this); - mListView.setLayoutParams(new ViewGroup.LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.MATCH_PARENT)); - mListView.setDrawSelectorOnTop(false); - mListView.setItemsCanFocus(true); - mListView.setLayoutParams((new LinearLayout.LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, - 0, - 1f))); - - List<String> bodies = Lists.newArrayList( - getBody("zero hello, my name is android"), - getBody("one i'm a paranoid android"), - getBody("two i robot. huh huh."), - getBody("three not the g-phone!")); - - mListView.setAdapter(new MyAdapter(this, bodies)); - mLinearLayout.addView(mListView); - - // add button below - Button buttonBelow = new Button(this); - buttonBelow.setLayoutParams( - new LinearLayout.LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.WRAP_CONTENT)); - buttonBelow.setText("button below list"); - mLinearLayout.addView(buttonBelow); - - setContentView(mLinearLayout); - } - - String getBody(String line) { - StringBuilder sb = new StringBuilder((line.length() + 5) * mLinesPerEditText); - for (int i = 0; i < mLinesPerEditText; i++) { - sb.append(i + 1).append(' ').append(line); - if (i < mLinesPerEditText - 1) { - sb.append('\n'); // all but last line - } - } - return sb.toString(); - } - - - private static class MyAdapter extends ArrayAdapter<String> { - - public MyAdapter(Context context, List<String> bodies) { - super(context, 0, bodies); - } - - @Override - public View getView(int position, View convertView, ViewGroup parent) { - String body = getItem(position); - - if (convertView != null) { - ((EditText) convertView).setText(body); - return convertView; - } - - EditText editText = new EditText(getContext()); - editText.setText(body); - return editText; - } - } -} diff --git a/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java b/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java index ece5037de15f..e17800f7b6fd 100644 --- a/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java +++ b/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java @@ -16,7 +16,7 @@ package com.android.internal.jank; -import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_GESTURE; +import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE; import static com.google.common.truth.Truth.assertThat; @@ -75,20 +75,13 @@ public class FrameTrackerTest { doNothing().when(mRenderer).addObserver(any()); doNothing().when(mRenderer).removeObserver(any()); - Session session = new Session(CUJ_NOTIFICATION_SHADE_GESTURE); - mTracker = Mockito.spy(new FrameTracker(session, handler, mRenderer, mWrapper)); + Session session = new Session(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE); + mTracker = Mockito.spy( + new FrameTracker(session, handler, mRenderer, mWrapper)); doNothing().when(mTracker).triggerPerfetto(); } @Test - public void testIgnoresSecondBegin() { - // Observer should be only added once in continuous calls. - mTracker.begin(); - mTracker.begin(); - verify(mRenderer, only()).addObserver(any()); - } - - @Test public void testOnlyFirstFrameOverThreshold() { // Just provide current timestamp anytime mWrapper asked for VSYNC_TIMESTAMP when(mWrapper.getMetric(FrameMetrics.VSYNC_TIMESTAMP)) @@ -170,6 +163,29 @@ public class FrameTrackerTest { verify(mTracker).triggerPerfetto(); } + @Test + public void testBeginCancel() { + mTracker.begin(); + verify(mRenderer).addObserver(any()); + + // First frame - not janky + setupFirstFrameMockWithDuration(4); + mTracker.onFrameMetricsAvailable(0); + + // normal frame - not janky + setupOtherFrameMockWithDuration(12); + mTracker.onFrameMetricsAvailable(0); + + // a janky frame + setupOtherFrameMockWithDuration(30); + mTracker.onFrameMetricsAvailable(0); + + mTracker.cancel(); + verify(mRenderer).removeObserver(any()); + // Since the tracker has been cancelled, shouldn't trigger perfetto. + verify(mTracker, never()).triggerPerfetto(); + } + private void setupFirstFrameMockWithDuration(long durationMillis) { doReturn(1L).when(mWrapper).getMetric(FrameMetrics.FIRST_DRAW_FRAME); doReturn(TimeUnit.MILLISECONDS.toNanos(durationMillis)) diff --git a/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java b/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java index 5c0b0c94f6d0..b669cc609baf 100644 --- a/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java +++ b/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java @@ -16,15 +16,20 @@ package com.android.internal.jank; -import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_GESTURE; +import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE; import static com.google.common.truth.Truth.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import android.os.Handler; import android.os.HandlerThread; +import android.os.Message; import android.view.View; import android.view.ViewAttachTestActivity; @@ -38,17 +43,13 @@ import com.android.internal.jank.InteractionJankMonitor.Session; import org.junit.Before; import org.junit.Rule; import org.junit.Test; -import org.mockito.Mockito; -import org.testng.Assert; - -import java.util.HashMap; -import java.util.Map; +import org.mockito.ArgumentCaptor; @SmallTest public class InteractionJankMonitorTest { private ViewAttachTestActivity mActivity; private View mView; - private FrameTracker mTracker; + private HandlerThread mWorker; @Rule public ActivityTestRule<ViewAttachTestActivity> mRule = @@ -61,55 +62,52 @@ public class InteractionJankMonitorTest { mView = mActivity.getWindow().getDecorView(); assertThat(mView.isAttachedToWindow()).isTrue(); - InteractionJankMonitor.reset(); + InteractionJankMonitor.abandon(); - // Prepare a FrameTracker to inject. - Session session = new Session(CUJ_NOTIFICATION_SHADE_GESTURE); - FrameMetricsWrapper wrapper = Mockito.spy(new FrameTracker.FrameMetricsWrapper()); - ThreadedRendererWrapper renderer = - Mockito.spy(new ThreadedRendererWrapper(mView.getThreadedRenderer())); - Handler handler = mActivity.getMainThreadHandler(); - mTracker = Mockito.spy(new FrameTracker(session, handler, renderer, wrapper)); + Handler handler = spy(new Handler(mActivity.getMainLooper())); + doReturn(true).when(handler).sendMessageAtTime(any(), anyLong()); + mWorker = spy(new HandlerThread("Interaction-jank-monitor-test")); + doNothing().when(mWorker).start(); + doReturn(handler).when(mWorker).getThreadHandler(); } @Test public void testBeginEnd() { - // Should throw exception if the view is not attached. - Assert.assertThrows(IllegalStateException.class, - () -> InteractionJankMonitor.init(new View(mActivity))); + // Should return false if the view is not attached. + InteractionJankMonitor monitor = spy(new InteractionJankMonitor(mWorker)); + assertThat(monitor.init(new View(mActivity))).isFalse(); // Verify we init InteractionJankMonitor correctly. - Map<String, FrameTracker> map = new HashMap<>(); - HandlerThread worker = Mockito.spy(new HandlerThread("Aot-test")); - doNothing().when(worker).start(); - InteractionJankMonitor.init(mView, mView.getThreadedRenderer(), map, worker); - verify(worker).start(); + assertThat(monitor.init(mView)).isTrue(); + verify(mWorker).start(); + + Session session = new Session(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE); + FrameTracker tracker = spy(new FrameTracker(session, mWorker.getThreadHandler(), + new ThreadedRendererWrapper(mView.getThreadedRenderer()), + new FrameMetricsWrapper())); + doReturn(tracker).when(monitor).createFrameTracker(any()); // Simulate a trace session and see if begin / end are invoked. - Session session = new Session(CUJ_NOTIFICATION_SHADE_GESTURE); - assertThat(map.get(session.getName())).isNull(); - InteractionJankMonitor.begin(CUJ_NOTIFICATION_SHADE_GESTURE, mTracker); - verify(mTracker).begin(); - assertThat(map.get(session.getName())).isEqualTo(mTracker); - InteractionJankMonitor.end(CUJ_NOTIFICATION_SHADE_GESTURE); - verify(mTracker).end(); - assertThat(map.get(session.getName())).isNull(); + assertThat(monitor.begin(session.getCuj())).isTrue(); + verify(tracker).begin(); + assertThat(monitor.end(session.getCuj())).isTrue(); + verify(tracker).end(); } @Test public void testCheckInitState() { - // Should throw exception if invoking begin / end without init invocation. - Assert.assertThrows(IllegalStateException.class, - () -> InteractionJankMonitor.begin(CUJ_NOTIFICATION_SHADE_GESTURE)); - Assert.assertThrows(IllegalStateException.class, - () -> InteractionJankMonitor.end(CUJ_NOTIFICATION_SHADE_GESTURE)); + InteractionJankMonitor monitor = new InteractionJankMonitor(mWorker); + + // Should return false if invoking begin / end without init invocation. + assertThat(monitor.begin(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE)).isFalse(); + assertThat(monitor.end(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE)).isFalse(); // Everything should be fine if invoking init first. boolean thrown = false; try { - InteractionJankMonitor.init(mActivity.getWindow().getDecorView()); - InteractionJankMonitor.begin(CUJ_NOTIFICATION_SHADE_GESTURE); - InteractionJankMonitor.end(CUJ_NOTIFICATION_SHADE_GESTURE); + monitor.init(mView); + assertThat(monitor.begin(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE)).isTrue(); + assertThat(monitor.end(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE)).isTrue(); } catch (Exception ex) { thrown = true; } finally { @@ -117,4 +115,27 @@ public class InteractionJankMonitorTest { } } + @Test + public void testBeginCancel() { + InteractionJankMonitor monitor = spy(new InteractionJankMonitor(mWorker)); + + ArgumentCaptor<Message> captor = ArgumentCaptor.forClass(Message.class); + assertThat(monitor.init(mView)).isTrue(); + + Session session = new Session(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE); + FrameTracker tracker = spy(new FrameTracker(session, mWorker.getThreadHandler(), + new ThreadedRendererWrapper(mView.getThreadedRenderer()), + new FrameMetricsWrapper())); + doReturn(tracker).when(monitor).createFrameTracker(any()); + + assertThat(monitor.begin(session.getCuj())).isTrue(); + verify(tracker).begin(); + verify(mWorker.getThreadHandler()).sendMessageAtTime(captor.capture(), anyLong()); + Runnable runnable = captor.getValue().getCallback(); + assertThat(runnable).isNotNull(); + mWorker.getThreadHandler().removeCallbacks(runnable); + runnable.run(); + verify(tracker).cancel(); + } + } diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsBinderCallStatsTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsBinderCallStatsTest.java index 22c41f3c9622..85f9c97629e3 100644 --- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsBinderCallStatsTest.java +++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsBinderCallStatsTest.java @@ -60,7 +60,7 @@ public class BatteryStatsBinderCallStatsTest extends TestCase { stat1.cpuTimeMicros = 1000; callStats.add(stat1); - bi.noteBinderCallStats(workSourceUid, 42, callStats, null); + bi.noteBinderCallStats(workSourceUid, 42, callStats); callStats.clear(); BinderCallsStats.CallStat stat2 = new BinderCallsStats.CallStat(callingUid, @@ -70,7 +70,7 @@ public class BatteryStatsBinderCallStatsTest extends TestCase { stat2.cpuTimeMicros = 500; callStats.add(stat2); - bi.noteBinderCallStats(workSourceUid, 8, callStats, null); + bi.noteBinderCallStats(workSourceUid, 8, callStats); BatteryStatsImpl.Uid uid = bi.getUidStatsLocked(workSourceUid); assertEquals(42 + 8, uid.getBinderCallCount()); @@ -112,7 +112,7 @@ public class BatteryStatsBinderCallStatsTest extends TestCase { stat1b.cpuTimeMicros = 1500; callStats.add(stat1b); - bi.noteBinderCallStats(workSourceUid1, 65, callStats, null); + bi.noteBinderCallStats(workSourceUid1, 65, callStats); // No recorded stats for some methods. Must use the global average. callStats.clear(); @@ -121,11 +121,11 @@ public class BatteryStatsBinderCallStatsTest extends TestCase { stat2.incrementalCallCount = 10; callStats.add(stat2); - bi.noteBinderCallStats(workSourceUid2, 40, callStats, null); + bi.noteBinderCallStats(workSourceUid2, 40, callStats); // No stats for any calls. Must use the global average callStats.clear(); - bi.noteBinderCallStats(workSourceUid3, 50, callStats, null); + bi.noteBinderCallStats(workSourceUid3, 50, callStats); bi.updateSystemServiceCallStats(); diff --git a/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java b/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java index 96250db4aa51..0eb34a993dec 100644 --- a/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java +++ b/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java @@ -47,8 +47,10 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Random; +import java.util.Set; @SmallTest @RunWith(AndroidJUnit4.class) @@ -770,13 +772,27 @@ public class BinderCallsStatsTest { bcs.setSamplingInterval(1); bcs.setTrackScreenInteractive(false); + final ArrayList<BinderCallsStats.CallStat> callStatsList = new ArrayList<>(); - bcs.setCallStatsObserver( - (workSourceUid, incrementalCallCount, callStats, binderThreadIds) -> - callStatsList.addAll(callStats)); + final Set<Integer> nativeTids = new HashSet<>(); + bcs.setCallStatsObserver(new BinderInternal.CallStatsObserver() { + @Override + public void noteCallStats(int workSourceUid, long incrementalCallCount, + Collection<BinderCallsStats.CallStat> callStats) { + callStatsList.addAll(callStats); + } + + @Override + public void noteBinderThreadNativeIds(int[] binderThreadNativeTids) { + for (int tid : binderThreadNativeTids) { + nativeTids.add(tid); + } + } + }); Binder binder = new Binder(); + bcs.nativeTid = 1000; CallSession callSession = bcs.callStarted(binder, 1, WORKSOURCE_UID); bcs.time += 10; bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID); @@ -785,6 +801,7 @@ public class BinderCallsStatsTest { bcs.time += 20; bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID); + bcs.nativeTid = 2000; callSession = bcs.callStarted(binder, 2, WORKSOURCE_UID); bcs.time += 30; bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID); @@ -809,6 +826,10 @@ public class BinderCallsStatsTest { assertEquals(30, callStats.maxCpuTimeMicros); } } + + assertEquals(2, nativeTids.size()); + assertTrue(nativeTids.contains(1000)); + assertTrue(nativeTids.contains(2000)); } @Test diff --git a/core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java index ac5443e1c7ce..2eee140b921f 100644 --- a/core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java +++ b/core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java @@ -87,7 +87,7 @@ public class SystemServicePowerCalculatorTest { stat1.cpuTimeMicros = 1000000; callStats.add(stat1); - mMockBatteryStats.noteBinderCallStats(workSourceUid1, 100, callStats, null); + mMockBatteryStats.noteBinderCallStats(workSourceUid1, 100, callStats); callStats.clear(); BinderCallsStats.CallStat stat2 = new BinderCallsStats.CallStat(workSourceUid2, @@ -97,7 +97,7 @@ public class SystemServicePowerCalculatorTest { stat2.cpuTimeMicros = 9000000; callStats.add(stat2); - mMockBatteryStats.noteBinderCallStats(workSourceUid2, 100, callStats, null); + mMockBatteryStats.noteBinderCallStats(workSourceUid2, 100, callStats); mMockBatteryStats.updateSystemServiceCallStats(); mMockBatteryStats.updateSystemServerThreadStats(); diff --git a/core/tests/utiltests/src/com/android/internal/util/CharSequencesTest.java b/core/tests/utiltests/src/com/android/internal/util/CharSequencesTest.java index 55d186c292f6..469a4ccd05e4 100644 --- a/core/tests/utiltests/src/com/android/internal/util/CharSequencesTest.java +++ b/core/tests/utiltests/src/com/android/internal/util/CharSequencesTest.java @@ -16,16 +16,17 @@ package com.android.internal.util; -import com.android.internal.util.CharSequences; import static com.android.internal.util.CharSequences.forAsciiBytes; -import junit.framework.TestCase; + import android.test.suitebuilder.annotation.SmallTest; +import junit.framework.TestCase; + public class CharSequencesTest extends TestCase { @SmallTest public void testCharSequences() { - String s = "Crazy Bob"; + String s = "Hello Bob"; byte[] bytes = s.getBytes(); String copy = toString(forAsciiBytes(bytes)); @@ -34,11 +35,11 @@ public class CharSequencesTest extends TestCase { copy = toString(forAsciiBytes(bytes, 0, s.length())); assertTrue(s.equals(copy)); - String crazy = toString(forAsciiBytes(bytes, 0, 5)); - assertTrue("Crazy".equals(crazy)); + String hello = toString(forAsciiBytes(bytes, 0, 5)); + assertTrue("Hello".equals(hello)); - String a = toString(forAsciiBytes(bytes, 0, 3).subSequence(2, 3)); - assertTrue("a".equals(a)); + String l = toString(forAsciiBytes(bytes, 0, 3).subSequence(2, 3)); + assertTrue("l".equals(l)); String empty = toString(forAsciiBytes(bytes, 0, 3).subSequence(3, 3)); assertTrue("".equals(empty)); diff --git a/data/etc/platform.xml b/data/etc/platform.xml index 0a0681479278..dd8f40d586bc 100644 --- a/data/etc/platform.xml +++ b/data/etc/platform.xml @@ -153,8 +153,8 @@ <assign-permission name="android.permission.UPDATE_APP_OPS_STATS" uid="media" /> <assign-permission name="android.permission.GET_PROCESS_STATE_AND_OOM_SCORE" uid="media" /> <assign-permission name="android.permission.PACKAGE_USAGE_STATS" uid="media" /> + <assign-permission name="android.permission.INTERNET" uid="media" /> - <assign-permission name="android.permission.INTERACT_ACROSS_USERS" uid="media" /> <assign-permission name="android.permission.INTERNET" uid="shell" /> @@ -164,7 +164,6 @@ <assign-permission name="android.permission.UPDATE_DEVICE_STATS" uid="audioserver" /> <assign-permission name="android.permission.UPDATE_APP_OPS_STATS" uid="audioserver" /> <assign-permission name="android.permission.PACKAGE_USAGE_STATS" uid="audioserver" /> - <assign-permission name="android.permission.INTERACT_ACROSS_USERS" uid="audioserver" /> <assign-permission name="android.permission.MODIFY_AUDIO_SETTINGS" uid="cameraserver" /> <assign-permission name="android.permission.ACCESS_SURFACE_FLINGER" uid="cameraserver" /> @@ -175,10 +174,8 @@ <assign-permission name="android.permission.PACKAGE_USAGE_STATS" uid="cameraserver" /> <assign-permission name="android.permission.WATCH_APPOPS" uid="cameraserver" /> <assign-permission name="android.permission.MANAGE_APP_OPS_MODES" uid="cameraserver" /> - <assign-permission name="android.permission.INTERACT_ACROSS_USERS" uid="cameraserver" /> <assign-permission name="android.permission.ACCESS_SURFACE_FLINGER" uid="graphics" /> - <assign-permission name="android.permission.INTERACT_ACROSS_USERS" uid="graphics" /> <assign-permission name="android.permission.DUMP" uid="incidentd" /> <assign-permission name="android.permission.PACKAGE_USAGE_STATS" uid="incidentd" /> @@ -193,10 +190,8 @@ <assign-permission name="android.permission.PACKAGE_USAGE_STATS" uid="statsd" /> <assign-permission name="android.permission.STATSCOMPANION" uid="statsd" /> <assign-permission name="android.permission.UPDATE_APP_OPS_STATS" uid="statsd" /> - <assign-permission name="android.permission.INTERACT_ACROSS_USERS" uid="statsd" /> <assign-permission name="android.permission.REGISTER_STATS_PULL_ATOM" uid="gpu_service" /> - <assign-permission name="android.permission.INTERACT_ACROSS_USERS" uid="gpu_service" /> <split-permission name="android.permission.ACCESS_FINE_LOCATION"> <new-permission name="android.permission.ACCESS_COARSE_LOCATION" /> diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json index 6b80bb60ec67..86e7adf945b7 100644 --- a/data/etc/services.core.protolog.json +++ b/data/etc/services.core.protolog.json @@ -151,6 +151,12 @@ "group": "WM_ERROR", "at": "com\/android\/server\/wm\/WindowManagerService.java" }, + "-1910833551": { + "message": "SyncSet{%x:%d} Start for %s", + "level": "VERBOSE", + "group": "WM_DEBUG_SYNC_ENGINE", + "at": "com\/android\/server\/wm\/BLASTSyncEngine.java" + }, "-1895337367": { "message": "Delete root task display=%d winMode=%d", "level": "VERBOSE", @@ -463,6 +469,12 @@ "group": "WM_ERROR", "at": "com\/android\/server\/wm\/WindowManagerService.java" }, + "-1387080937": { + "message": "SyncSet{%x:%d} Child ready, now ready=%b and waiting on %d transactions", + "level": "VERBOSE", + "group": "WM_DEBUG_SYNC_ENGINE", + "at": "com\/android\/server\/wm\/BLASTSyncEngine.java" + }, "-1364754753": { "message": "Task vanished taskId=%d", "level": "VERBOSE", @@ -481,6 +493,12 @@ "group": "WM_DEBUG_BOOT", "at": "com\/android\/server\/wm\/WindowManagerService.java" }, + "-1340783230": { + "message": "SyncSet{%x:%d} Added %s. now waiting on %d transactions", + "level": "VERBOSE", + "group": "WM_DEBUG_SYNC_ENGINE", + "at": "com\/android\/server\/wm\/BLASTSyncEngine.java" + }, "-1340540100": { "message": "Creating SnapshotStartingData", "level": "VERBOSE", @@ -859,6 +877,12 @@ "group": "WM_DEBUG_ORIENTATION", "at": "com\/android\/server\/wm\/DragState.java" }, + "-678300709": { + "message": "SyncSet{%x:%d} Trying to add %s", + "level": "VERBOSE", + "group": "WM_DEBUG_SYNC_ENGINE", + "at": "com\/android\/server\/wm\/BLASTSyncEngine.java" + }, "-668956537": { "message": " THUMBNAIL %s: CREATE", "level": "INFO", @@ -1735,6 +1759,12 @@ "group": "WM_DEBUG_IME", "at": "com\/android\/server\/wm\/ImeInsetsSourceProvider.java" }, + "590184240": { + "message": "- NOT adding to sync: visible=%b hasListener=%b", + "level": "VERBOSE", + "group": "WM_DEBUG_SYNC_ENGINE", + "at": "com\/android\/server\/wm\/WindowContainer.java" + }, "594260577": { "message": "createWallpaperAnimations()", "level": "DEBUG", @@ -2005,6 +2035,12 @@ "group": "WM_DEBUG_REMOTE_ANIMATIONS", "at": "com\/android\/server\/wm\/RemoteAnimationController.java" }, + "1000601037": { + "message": "SyncSet{%x:%d} Set ready", + "level": "VERBOSE", + "group": "WM_DEBUG_SYNC_ENGINE", + "at": "com\/android\/server\/wm\/BLASTSyncEngine.java" + }, "1001904964": { "message": "***** BOOT TIMEOUT: forcing display enabled", "level": "WARN", @@ -2599,6 +2635,12 @@ "group": "WM_DEBUG_CONFIGURATION", "at": "com\/android\/server\/wm\/ActivityRecord.java" }, + "2001924866": { + "message": "SyncSet{%x:%d} Finished. Reporting %d containers to %s", + "level": "VERBOSE", + "group": "WM_DEBUG_SYNC_ENGINE", + "at": "com\/android\/server\/wm\/BLASTSyncEngine.java" + }, "2016061474": { "message": "Prepare app transition: transit=%s %s alwaysKeepCurrent=%b displayId=%d Callers=%s", "level": "VERBOSE", @@ -2775,6 +2817,9 @@ "WM_DEBUG_SWITCH": { "tag": "WindowManager" }, + "WM_DEBUG_SYNC_ENGINE": { + "tag": "WindowManager" + }, "WM_DEBUG_WINDOW_MOVEMENT": { "tag": "WindowManager" }, diff --git a/graphics/java/android/graphics/BlurShader.java b/graphics/java/android/graphics/BlurShader.java index 779a89051060..3bc811983336 100644 --- a/graphics/java/android/graphics/BlurShader.java +++ b/graphics/java/android/graphics/BlurShader.java @@ -16,6 +16,7 @@ package android.graphics; +import android.annotation.NonNull; import android.annotation.Nullable; /** @@ -28,6 +29,7 @@ public final class BlurShader extends Shader { private final float mRadiusX; private final float mRadiusY; private final Shader mInputShader; + private final TileMode mEdgeTreatment; private long mNativeInputShader = 0; @@ -35,22 +37,42 @@ public final class BlurShader extends Shader { * Create a {@link BlurShader} that blurs the contents of the optional input shader * with the specified radius along the x and y axis. If no input shader is provided * then all drawing commands issued with a {@link android.graphics.Paint} that this - * shader is installed in will be blurred + * shader is installed in will be blurred. + * + * This uses a default {@link TileMode#DECAL} for edge treatment + * * @param radiusX Radius of blur along the X axis * @param radiusY Radius of blur along the Y axis * @param inputShader Input shader that provides the content to be blurred */ public BlurShader(float radiusX, float radiusY, @Nullable Shader inputShader) { + this(radiusX, radiusY, inputShader, TileMode.DECAL); + } + + /** + * Create a {@link BlurShader} that blurs the contents of the optional input shader + * with the specified radius along the x and y axis. If no input shader is provided + * then all drawing commands issued with a {@link android.graphics.Paint} that this + * shader is installed in will be blurred + * @param radiusX Radius of blur along the X axis + * @param radiusY Radius of blur along the Y axis + * @param inputShader Input shader that provides the content to be blurred + * @param edgeTreatment Policy for how to blur content near edges of the blur shader + */ + public BlurShader(float radiusX, float radiusY, @Nullable Shader inputShader, + @NonNull TileMode edgeTreatment) { mRadiusX = radiusX; mRadiusY = radiusY; mInputShader = inputShader; + mEdgeTreatment = edgeTreatment; } /** @hide **/ @Override protected long createNativeInstance(long nativeMatrix) { mNativeInputShader = mInputShader != null ? mInputShader.getNativeInstance() : 0; - return nativeCreate(nativeMatrix, mRadiusX, mRadiusY, mNativeInputShader); + return nativeCreate(nativeMatrix, mRadiusX, mRadiusY, mNativeInputShader, + mEdgeTreatment.nativeInt); } /** @hide **/ @@ -61,5 +83,5 @@ public final class BlurShader extends Shader { } private static native long nativeCreate(long nativeMatrix, float radiusX, float radiusY, - long inputShader); + long inputShader, int edgeTreatment); } diff --git a/graphics/java/android/graphics/ParcelableColorSpace.java b/graphics/java/android/graphics/ParcelableColorSpace.java index f9033a53d7e6..d408ac3718ca 100644 --- a/graphics/java/android/graphics/ParcelableColorSpace.java +++ b/graphics/java/android/graphics/ParcelableColorSpace.java @@ -25,8 +25,6 @@ import android.os.Parcelable; * A {@link Parcelable} {@link ColorSpace}. In order to enable parceling, the ColorSpace * must be either a {@link ColorSpace.Named Named} ColorSpace or a {@link ColorSpace.Rgb} instance * that has an ICC parametric transfer function as returned by {@link Rgb#getTransferParameters()}. - * TODO: Make public - * @hide */ public final class ParcelableColorSpace extends ColorSpace implements Parcelable { private final ColorSpace mColorSpace; diff --git a/graphics/java/android/graphics/Shader.java b/graphics/java/android/graphics/Shader.java index 8154ebf1e508..d71ff1138b25 100644 --- a/graphics/java/android/graphics/Shader.java +++ b/graphics/java/android/graphics/Shader.java @@ -95,7 +95,11 @@ public class Shader { * repeat the shader's image horizontally and vertically, alternating * mirror images so that adjacent images always seam */ - MIRROR (2); + MIRROR(2), + /** + * Only draw within the original domain, return transparent-black everywhere else + */ + DECAL(3); TileMode(int nativeInt) { this.nativeInt = nativeInt; diff --git a/libs/WindowManager/Shell/res/values/strings.xml b/libs/WindowManager/Shell/res/values/strings.xml index b6668fbe4872..da5965dab71a 100644 --- a/libs/WindowManager/Shell/res/values/strings.xml +++ b/libs/WindowManager/Shell/res/values/strings.xml @@ -93,4 +93,8 @@ <string name="one_handed_tutorial_title">Using one-handed mode</string> <!-- One-Handed Tutorial description [CHAR LIMIT=NONE] --> <string name="one_handed_tutorial_description">To exit, swipe up from the bottom of the screen or tap anywhere above the app</string> + <!-- Accessibility description for start one-handed mode [CHAR LIMIT=NONE] --> + <string name="accessibility_action_start_one_handed">Start one-handed mode</string> + <!-- Accessibility description for stop one-handed mode [CHAR LIMIT=NONE] --> + <string name="accessibility_action_stop_one_handed">Exit one-handed mode</string> </resources> diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTutorialHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTutorialHandler.java index b15b5154c2a4..7c0c738644b7 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTutorialHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTutorialHandler.java @@ -29,6 +29,8 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.WindowManager; +import android.view.accessibility.AccessibilityEvent; +import android.view.accessibility.AccessibilityManager; import android.widget.FrameLayout; import androidx.annotation.NonNull; @@ -50,12 +52,16 @@ public class OneHandedTutorialHandler implements OneHandedTransitionCallback { private static final int MAX_TUTORIAL_SHOW_COUNT = 2; private final Rect mLastUpdatedBounds = new Rect(); private final WindowManager mWindowManager; + private final AccessibilityManager mAccessibilityManager; + private final String mPackageName; private View mTutorialView; private Point mDisplaySize = new Point(); private Handler mUpdateHandler; private ContentResolver mContentResolver; private boolean mCanShowTutorial; + private String mStartOneHandedDescription; + private String mStopOneHandedDescription; /** * Container of the tutorial panel showing at outside region when one handed starting @@ -72,9 +78,12 @@ public class OneHandedTutorialHandler implements OneHandedTransitionCallback { public OneHandedTutorialHandler(Context context) { context.getDisplay().getRealSize(mDisplaySize); + mPackageName = context.getPackageName(); mContentResolver = context.getContentResolver(); mUpdateHandler = new Handler(); mWindowManager = context.getSystemService(WindowManager.class); + mAccessibilityManager = (AccessibilityManager) + context.getSystemService(Context.ACCESSIBILITY_SERVICE); mTargetViewContainer = new FrameLayout(context); mTargetViewContainer.setClipChildren(false); mTutorialAreaHeight = Math.round(mDisplaySize.y @@ -84,6 +93,10 @@ public class OneHandedTutorialHandler implements OneHandedTransitionCallback { mCanShowTutorial = (Settings.Secure.getInt(mContentResolver, Settings.Secure.ONE_HANDED_TUTORIAL_SHOW_COUNT, 0) >= MAX_TUTORIAL_SHOW_COUNT) ? false : true; + mStartOneHandedDescription = context.getResources().getString( + R.string.accessibility_action_start_one_handed); + mStopOneHandedDescription = context.getResources().getString( + R.string.accessibility_action_stop_one_handed); if (mCanShowTutorial) { createOrUpdateTutorialTarget(); } @@ -94,13 +107,16 @@ public class OneHandedTutorialHandler implements OneHandedTransitionCallback { mUpdateHandler.post(() -> { updateFinished(View.VISIBLE, 0f); updateTutorialCount(); + announcementForScreenReader(true); }); } @Override public void onStopFinished(Rect bounds) { - mUpdateHandler.post(() -> updateFinished( - View.INVISIBLE, -mTargetViewContainer.getHeight())); + mUpdateHandler.post(() -> { + updateFinished(View.INVISIBLE, -mTargetViewContainer.getHeight()); + announcementForScreenReader(false); + }); } private void updateFinished(int visible, float finalPosition) { @@ -121,6 +137,17 @@ public class OneHandedTutorialHandler implements OneHandedTransitionCallback { Settings.Secure.ONE_HANDED_TUTORIAL_SHOW_COUNT, showCount); } + private void announcementForScreenReader(boolean isStartOneHanded) { + if (mAccessibilityManager.isTouchExplorationEnabled()) { + final AccessibilityEvent event = AccessibilityEvent.obtain(); + event.setPackageName(mPackageName); + event.setEventType(AccessibilityEvent.TYPE_ANNOUNCEMENT); + event.getText().add(isStartOneHanded + ? mStartOneHandedDescription : mStopOneHandedDescription); + mAccessibilityManager.sendAccessibilityEvent(event); + } + } + /** * Adds the tutorial target view to the WindowManager and update its layout, so it's ready * to be animated in. diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp index cfba5d4f6aa2..a690840e91a9 100644 --- a/libs/hwui/SkiaCanvas.cpp +++ b/libs/hwui/SkiaCanvas.cpp @@ -735,8 +735,7 @@ void SkiaCanvas::drawVectorDrawable(VectorDrawableRoot* vectorDrawable) { // ---------------------------------------------------------------------------- void SkiaCanvas::drawGlyphs(ReadGlyphFunc glyphFunc, int count, const Paint& paint, float x, - float y, float boundsLeft, float boundsTop, float boundsRight, - float boundsBottom, float totalAdvance) { + float y, float totalAdvance) { if (count <= 0 || paint.nothingToDraw()) return; Paint paintCopy(paint); if (mPaintFilter) { diff --git a/libs/hwui/SkiaCanvas.h b/libs/hwui/SkiaCanvas.h index 1df2b2671659..2cb850c83934 100644 --- a/libs/hwui/SkiaCanvas.h +++ b/libs/hwui/SkiaCanvas.h @@ -161,8 +161,7 @@ protected: void drawDrawable(SkDrawable* drawable) { mCanvas->drawDrawable(drawable); } virtual void drawGlyphs(ReadGlyphFunc glyphFunc, int count, const Paint& paint, float x, - float y, float boundsLeft, float boundsTop, float boundsRight, - float boundsBottom, float totalAdvance) override; + float y, float totalAdvance) override; virtual void drawLayoutOnPath(const minikin::Layout& layout, float hOffset, float vOffset, const Paint& paint, const SkPath& path, size_t start, size_t end) override; diff --git a/libs/hwui/hwui/Canvas.cpp b/libs/hwui/hwui/Canvas.cpp index 2a377bbb83f2..2001b5620f84 100644 --- a/libs/hwui/hwui/Canvas.cpp +++ b/libs/hwui/hwui/Canvas.cpp @@ -84,13 +84,12 @@ static void simplifyPaint(int color, Paint* paint) { class DrawTextFunctor { public: DrawTextFunctor(const minikin::Layout& layout, Canvas* canvas, const Paint& paint, float x, - float y, minikin::MinikinRect& bounds, float totalAdvance) + float y, float totalAdvance) : layout(layout) , canvas(canvas) , paint(paint) , x(x) , y(y) - , bounds(bounds) , totalAdvance(totalAdvance) {} void operator()(size_t start, size_t end) { @@ -114,19 +113,16 @@ public: Paint outlinePaint(paint); simplifyPaint(darken ? SK_ColorWHITE : SK_ColorBLACK, &outlinePaint); outlinePaint.setStyle(SkPaint::kStrokeAndFill_Style); - canvas->drawGlyphs(glyphFunc, glyphCount, outlinePaint, x, y, bounds.mLeft, bounds.mTop, - bounds.mRight, bounds.mBottom, totalAdvance); + canvas->drawGlyphs(glyphFunc, glyphCount, outlinePaint, x, y, totalAdvance); // inner Paint innerPaint(paint); simplifyPaint(darken ? SK_ColorBLACK : SK_ColorWHITE, &innerPaint); innerPaint.setStyle(SkPaint::kFill_Style); - canvas->drawGlyphs(glyphFunc, glyphCount, innerPaint, x, y, bounds.mLeft, bounds.mTop, - bounds.mRight, bounds.mBottom, totalAdvance); + canvas->drawGlyphs(glyphFunc, glyphCount, innerPaint, x, y, totalAdvance); } else { // standard draw path - canvas->drawGlyphs(glyphFunc, glyphCount, paint, x, y, bounds.mLeft, bounds.mTop, - bounds.mRight, bounds.mBottom, totalAdvance); + canvas->drawGlyphs(glyphFunc, glyphCount, paint, x, y, totalAdvance); } } @@ -136,7 +132,6 @@ private: const Paint& paint; float x; float y; - minikin::MinikinRect& bounds; float totalAdvance; }; @@ -156,15 +151,12 @@ void Canvas::drawText(const uint16_t* text, int textSize, int start, int count, x += MinikinUtils::xOffsetForTextAlign(&paint, layout); - minikin::MinikinRect bounds; - layout.getBounds(&bounds); - // Set align to left for drawing, as we don't want individual // glyphs centered or right-aligned; the offset above takes // care of all alignment. paint.setTextAlign(Paint::kLeft_Align); - DrawTextFunctor f(layout, this, paint, x, y, bounds, layout.getAdvance()); + DrawTextFunctor f(layout, this, paint, x, y, layout.getAdvance()); MinikinUtils::forFontRun(layout, &paint, f); } diff --git a/libs/hwui/hwui/Canvas.h b/libs/hwui/hwui/Canvas.h index 333567b0cf91..817c7ee9077f 100644 --- a/libs/hwui/hwui/Canvas.h +++ b/libs/hwui/hwui/Canvas.h @@ -288,8 +288,7 @@ protected: * totalAdvance: used to define width of text decorations (underlines, strikethroughs). */ virtual void drawGlyphs(ReadGlyphFunc glyphFunc, int count, const Paint& paint, float x, - float y, float boundsLeft, float boundsTop, float boundsRight, - float boundsBottom, float totalAdvance) = 0; + float y,float totalAdvance) = 0; virtual void drawLayoutOnPath(const minikin::Layout& layout, float hOffset, float vOffset, const Paint& paint, const SkPath& path, size_t start, size_t end) = 0; diff --git a/libs/hwui/jni/Shader.cpp b/libs/hwui/jni/Shader.cpp index 7cb77233846f..0a194f9dd666 100644 --- a/libs/hwui/jni/Shader.cpp +++ b/libs/hwui/jni/Shader.cpp @@ -224,7 +224,7 @@ static jlong ComposeShader_create(JNIEnv* env, jobject o, jlong matrixPtr, /////////////////////////////////////////////////////////////////////////////////////////////// static jlong BlurShader_create(JNIEnv* env , jobject o, jlong matrixPtr, jfloat sigmaX, - jfloat sigmaY, jlong shaderHandle) { + jfloat sigmaY, jlong shaderHandle, jint edgeTreatment) { auto* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr); auto* inputShader = reinterpret_cast<Shader*>(shaderHandle); @@ -232,6 +232,7 @@ static jlong BlurShader_create(JNIEnv* env , jobject o, jlong matrixPtr, jfloat sigmaX, sigmaY, inputShader, + static_cast<SkTileMode>(edgeTreatment), matrix ); return reinterpret_cast<jlong>(blurShader); @@ -291,7 +292,7 @@ static const JNINativeMethod gBitmapShaderMethods[] = { }; static const JNINativeMethod gBlurShaderMethods[] = { - { "nativeCreate", "(JFFJ)J", (void*)BlurShader_create } + { "nativeCreate", "(JFFJI)J", (void*)BlurShader_create } }; static const JNINativeMethod gLinearGradientMethods[] = { diff --git a/libs/hwui/shader/BlurShader.cpp b/libs/hwui/shader/BlurShader.cpp index fa10be100bca..2abd8714204b 100644 --- a/libs/hwui/shader/BlurShader.cpp +++ b/libs/hwui/shader/BlurShader.cpp @@ -20,13 +20,14 @@ #include "utils/Blur.h" namespace android::uirenderer { -BlurShader::BlurShader(float radiusX, float radiusY, Shader* inputShader, const SkMatrix* matrix) +BlurShader::BlurShader(float radiusX, float radiusY, Shader* inputShader, SkTileMode edgeTreatment, + const SkMatrix* matrix) : Shader(matrix) , skImageFilter( SkImageFilters::Blur( Blur::convertRadiusToSigma(radiusX), Blur::convertRadiusToSigma(radiusY), - SkTileMode::kClamp, + edgeTreatment, inputShader ? inputShader->asSkImageFilter() : nullptr, nullptr) ) { } diff --git a/libs/hwui/shader/BlurShader.h b/libs/hwui/shader/BlurShader.h index 9eb22bd11f4a..60a15898893e 100644 --- a/libs/hwui/shader/BlurShader.h +++ b/libs/hwui/shader/BlurShader.h @@ -30,8 +30,12 @@ public: * * This will blur the contents of the provided input shader if it is non-null, otherwise * the source bitmap will be blurred instead. + * + * The edge treatment parameter determines how content near the edges of the source is to + * participate in the blur */ - BlurShader(float radiusX, float radiusY, Shader* inputShader, const SkMatrix* matrix); + BlurShader(float radiusX, float radiusY, Shader* inputShader, SkTileMode edgeTreatment, + const SkMatrix* matrix); ~BlurShader() override; protected: sk_sp<SkImageFilter> makeSkImageFilter() override; diff --git a/media/Android.bp b/media/Android.bp index 8895b3a9a2ba..828707b70e7b 100644 --- a/media/Android.bp +++ b/media/Android.bp @@ -1,85 +1,52 @@ aidl_interface { name: "audio_common-aidl", unstable: true, - local_include_dir: "java", + local_include_dir: "aidl", srcs: [ - "java/android/media/audio/common/AudioChannelMask.aidl", - "java/android/media/audio/common/AudioConfig.aidl", - "java/android/media/audio/common/AudioFormat.aidl", - "java/android/media/audio/common/AudioOffloadInfo.aidl", - "java/android/media/audio/common/AudioStreamType.aidl", - "java/android/media/audio/common/AudioUsage.aidl", + "aidl/android/media/audio/common/AudioChannelMask.aidl", + "aidl/android/media/audio/common/AudioConfig.aidl", + "aidl/android/media/audio/common/AudioFormat.aidl", + "aidl/android/media/audio/common/AudioOffloadInfo.aidl", + "aidl/android/media/audio/common/AudioStreamType.aidl", + "aidl/android/media/audio/common/AudioUsage.aidl", ], - backend: - { - cpp: { - enabled: true, - }, - java: { - // Already generated as part of the entire media java library. - enabled: false, - }, - }, } aidl_interface { name: "media_permission-aidl", unstable: true, - local_include_dir: "java", + local_include_dir: "aidl", srcs: [ - "java/android/media/permission/Identity.aidl", + "aidl/android/media/permission/Identity.aidl", ], - backend: - { - cpp: { - enabled: true, - }, - java: { - // Already generated as part of the entire media java library. - enabled: false, - }, - }, } aidl_interface { name: "soundtrigger_middleware-aidl", unstable: true, - local_include_dir: "java", + local_include_dir: "aidl", srcs: [ - "java/android/media/soundtrigger_middleware/ConfidenceLevel.aidl", - "java/android/media/soundtrigger_middleware/ISoundTriggerCallback.aidl", - "java/android/media/soundtrigger_middleware/ISoundTriggerMiddlewareService.aidl", - "java/android/media/soundtrigger_middleware/ISoundTriggerModule.aidl", - "java/android/media/soundtrigger_middleware/ModelParameter.aidl", - "java/android/media/soundtrigger_middleware/ModelParameterRange.aidl", - "java/android/media/soundtrigger_middleware/Phrase.aidl", - "java/android/media/soundtrigger_middleware/PhraseRecognitionEvent.aidl", - "java/android/media/soundtrigger_middleware/PhraseRecognitionExtra.aidl", - "java/android/media/soundtrigger_middleware/PhraseSoundModel.aidl", - "java/android/media/soundtrigger_middleware/RecognitionConfig.aidl", - "java/android/media/soundtrigger_middleware/RecognitionEvent.aidl", - "java/android/media/soundtrigger_middleware/RecognitionMode.aidl", - "java/android/media/soundtrigger_middleware/RecognitionStatus.aidl", - "java/android/media/soundtrigger_middleware/SoundModel.aidl", - "java/android/media/soundtrigger_middleware/SoundModelType.aidl", - "java/android/media/soundtrigger_middleware/SoundTriggerModuleDescriptor.aidl", - "java/android/media/soundtrigger_middleware/SoundTriggerModuleProperties.aidl", - "java/android/media/soundtrigger_middleware/Status.aidl", + "aidl/android/media/soundtrigger_middleware/AudioCapabilities.aidl", + "aidl/android/media/soundtrigger_middleware/ConfidenceLevel.aidl", + "aidl/android/media/soundtrigger_middleware/ISoundTriggerCallback.aidl", + "aidl/android/media/soundtrigger_middleware/ISoundTriggerMiddlewareService.aidl", + "aidl/android/media/soundtrigger_middleware/ISoundTriggerModule.aidl", + "aidl/android/media/soundtrigger_middleware/ModelParameter.aidl", + "aidl/android/media/soundtrigger_middleware/ModelParameterRange.aidl", + "aidl/android/media/soundtrigger_middleware/Phrase.aidl", + "aidl/android/media/soundtrigger_middleware/PhraseRecognitionEvent.aidl", + "aidl/android/media/soundtrigger_middleware/PhraseRecognitionExtra.aidl", + "aidl/android/media/soundtrigger_middleware/PhraseSoundModel.aidl", + "aidl/android/media/soundtrigger_middleware/RecognitionConfig.aidl", + "aidl/android/media/soundtrigger_middleware/RecognitionEvent.aidl", + "aidl/android/media/soundtrigger_middleware/RecognitionMode.aidl", + "aidl/android/media/soundtrigger_middleware/RecognitionStatus.aidl", + "aidl/android/media/soundtrigger_middleware/SoundModel.aidl", + "aidl/android/media/soundtrigger_middleware/SoundModelType.aidl", + "aidl/android/media/soundtrigger_middleware/SoundTriggerModuleDescriptor.aidl", + "aidl/android/media/soundtrigger_middleware/SoundTriggerModuleProperties.aidl", + "aidl/android/media/soundtrigger_middleware/Status.aidl", ], - backend: - { - cpp: { - enabled: true, - }, - java: { - // Already generated as part of the entire media java library. - enabled: false, - }, - ndk: { - // Not currently needed, and disabled because of b/146172425 - enabled: false, - }, - }, imports: [ "audio_common-aidl", "media_permission-aidl", diff --git a/media/OWNERS b/media/OWNERS index c95ac6c210c0..36df3a05e0ee 100644 --- a/media/OWNERS +++ b/media/OWNERS @@ -9,6 +9,7 @@ hkuang@google.com hunga@google.com insun@google.com jaewan@google.com +jinpark@google.com jmtrivi@google.com jsharkey@android.com klhyun@google.com @@ -17,6 +18,3 @@ marcone@google.com philburk@google.com sungsoo@google.com wonsik@google.com - -# For maintaining sync with AndroidX code -per-file ExifInterface.java = jinpark@google.com, sungsoo@google.com diff --git a/media/java/android/media/audio/common/AudioChannelMask.aidl b/media/aidl/android/media/audio/common/AudioChannelMask.aidl index b9b08e6921bc..b9b08e6921bc 100644 --- a/media/java/android/media/audio/common/AudioChannelMask.aidl +++ b/media/aidl/android/media/audio/common/AudioChannelMask.aidl diff --git a/media/java/android/media/audio/common/AudioConfig.aidl b/media/aidl/android/media/audio/common/AudioConfig.aidl index 50dd796e1fa0..50dd796e1fa0 100644 --- a/media/java/android/media/audio/common/AudioConfig.aidl +++ b/media/aidl/android/media/audio/common/AudioConfig.aidl diff --git a/media/java/android/media/audio/common/AudioFormat.aidl b/media/aidl/android/media/audio/common/AudioFormat.aidl index aadc8e26cce3..aadc8e26cce3 100644 --- a/media/java/android/media/audio/common/AudioFormat.aidl +++ b/media/aidl/android/media/audio/common/AudioFormat.aidl diff --git a/media/java/android/media/audio/common/AudioOffloadInfo.aidl b/media/aidl/android/media/audio/common/AudioOffloadInfo.aidl index ec10d71135ae..ec10d71135ae 100644 --- a/media/java/android/media/audio/common/AudioOffloadInfo.aidl +++ b/media/aidl/android/media/audio/common/AudioOffloadInfo.aidl diff --git a/media/java/android/media/audio/common/AudioStreamType.aidl b/media/aidl/android/media/audio/common/AudioStreamType.aidl index c54566726350..c54566726350 100644 --- a/media/java/android/media/audio/common/AudioStreamType.aidl +++ b/media/aidl/android/media/audio/common/AudioStreamType.aidl diff --git a/media/java/android/media/audio/common/AudioUsage.aidl b/media/aidl/android/media/audio/common/AudioUsage.aidl index ef348165b22c..ef348165b22c 100644 --- a/media/java/android/media/audio/common/AudioUsage.aidl +++ b/media/aidl/android/media/audio/common/AudioUsage.aidl diff --git a/media/java/android/media/permission/Identity.aidl b/media/aidl/android/media/permission/Identity.aidl index 361497d59ea9..361497d59ea9 100644 --- a/media/java/android/media/permission/Identity.aidl +++ b/media/aidl/android/media/permission/Identity.aidl diff --git a/media/java/android/media/soundtrigger_middleware/AudioCapabilities.aidl b/media/aidl/android/media/soundtrigger_middleware/AudioCapabilities.aidl index 97a8849c7b07..97a8849c7b07 100644 --- a/media/java/android/media/soundtrigger_middleware/AudioCapabilities.aidl +++ b/media/aidl/android/media/soundtrigger_middleware/AudioCapabilities.aidl diff --git a/media/java/android/media/soundtrigger_middleware/ConfidenceLevel.aidl b/media/aidl/android/media/soundtrigger_middleware/ConfidenceLevel.aidl index 3dbc70556bd3..3dbc70556bd3 100644 --- a/media/java/android/media/soundtrigger_middleware/ConfidenceLevel.aidl +++ b/media/aidl/android/media/soundtrigger_middleware/ConfidenceLevel.aidl diff --git a/media/java/android/media/soundtrigger_middleware/ISoundTriggerCallback.aidl b/media/aidl/android/media/soundtrigger_middleware/ISoundTriggerCallback.aidl index 726af7681979..726af7681979 100644 --- a/media/java/android/media/soundtrigger_middleware/ISoundTriggerCallback.aidl +++ b/media/aidl/android/media/soundtrigger_middleware/ISoundTriggerCallback.aidl diff --git a/media/java/android/media/soundtrigger_middleware/ISoundTriggerMiddlewareService.aidl b/media/aidl/android/media/soundtrigger_middleware/ISoundTriggerMiddlewareService.aidl index d1126b9006e0..d1126b9006e0 100644 --- a/media/java/android/media/soundtrigger_middleware/ISoundTriggerMiddlewareService.aidl +++ b/media/aidl/android/media/soundtrigger_middleware/ISoundTriggerMiddlewareService.aidl diff --git a/media/java/android/media/soundtrigger_middleware/ISoundTriggerModule.aidl b/media/aidl/android/media/soundtrigger_middleware/ISoundTriggerModule.aidl index c4a57857dd3d..c4a57857dd3d 100644 --- a/media/java/android/media/soundtrigger_middleware/ISoundTriggerModule.aidl +++ b/media/aidl/android/media/soundtrigger_middleware/ISoundTriggerModule.aidl diff --git a/media/java/android/media/soundtrigger_middleware/ModelParameter.aidl b/media/aidl/android/media/soundtrigger_middleware/ModelParameter.aidl index 09936278e93a..09936278e93a 100644 --- a/media/java/android/media/soundtrigger_middleware/ModelParameter.aidl +++ b/media/aidl/android/media/soundtrigger_middleware/ModelParameter.aidl diff --git a/media/java/android/media/soundtrigger_middleware/ModelParameterRange.aidl b/media/aidl/android/media/soundtrigger_middleware/ModelParameterRange.aidl index d6948a87dc6d..d6948a87dc6d 100644 --- a/media/java/android/media/soundtrigger_middleware/ModelParameterRange.aidl +++ b/media/aidl/android/media/soundtrigger_middleware/ModelParameterRange.aidl diff --git a/media/java/android/media/soundtrigger_middleware/OWNERS b/media/aidl/android/media/soundtrigger_middleware/OWNERS index e5d037003ac4..e5d037003ac4 100644 --- a/media/java/android/media/soundtrigger_middleware/OWNERS +++ b/media/aidl/android/media/soundtrigger_middleware/OWNERS diff --git a/media/java/android/media/soundtrigger_middleware/Phrase.aidl b/media/aidl/android/media/soundtrigger_middleware/Phrase.aidl index 98a489f8a6a9..98a489f8a6a9 100644 --- a/media/java/android/media/soundtrigger_middleware/Phrase.aidl +++ b/media/aidl/android/media/soundtrigger_middleware/Phrase.aidl diff --git a/media/java/android/media/soundtrigger_middleware/PhraseRecognitionEvent.aidl b/media/aidl/android/media/soundtrigger_middleware/PhraseRecognitionEvent.aidl index 6a3ec61d1ebf..6a3ec61d1ebf 100644 --- a/media/java/android/media/soundtrigger_middleware/PhraseRecognitionEvent.aidl +++ b/media/aidl/android/media/soundtrigger_middleware/PhraseRecognitionEvent.aidl diff --git a/media/java/android/media/soundtrigger_middleware/PhraseRecognitionExtra.aidl b/media/aidl/android/media/soundtrigger_middleware/PhraseRecognitionExtra.aidl index cb96bf37a95d..cb96bf37a95d 100644 --- a/media/java/android/media/soundtrigger_middleware/PhraseRecognitionExtra.aidl +++ b/media/aidl/android/media/soundtrigger_middleware/PhraseRecognitionExtra.aidl diff --git a/media/java/android/media/soundtrigger_middleware/PhraseSoundModel.aidl b/media/aidl/android/media/soundtrigger_middleware/PhraseSoundModel.aidl index 81028c1608ea..81028c1608ea 100644 --- a/media/java/android/media/soundtrigger_middleware/PhraseSoundModel.aidl +++ b/media/aidl/android/media/soundtrigger_middleware/PhraseSoundModel.aidl diff --git a/media/java/android/media/soundtrigger_middleware/RecognitionConfig.aidl b/media/aidl/android/media/soundtrigger_middleware/RecognitionConfig.aidl index 5c0eeb1e32b1..5c0eeb1e32b1 100644 --- a/media/java/android/media/soundtrigger_middleware/RecognitionConfig.aidl +++ b/media/aidl/android/media/soundtrigger_middleware/RecognitionConfig.aidl diff --git a/media/java/android/media/soundtrigger_middleware/RecognitionEvent.aidl b/media/aidl/android/media/soundtrigger_middleware/RecognitionEvent.aidl index a237ec1aa3b3..a237ec1aa3b3 100644 --- a/media/java/android/media/soundtrigger_middleware/RecognitionEvent.aidl +++ b/media/aidl/android/media/soundtrigger_middleware/RecognitionEvent.aidl diff --git a/media/java/android/media/soundtrigger_middleware/RecognitionMode.aidl b/media/aidl/android/media/soundtrigger_middleware/RecognitionMode.aidl index d8bfff4bec6f..d8bfff4bec6f 100644 --- a/media/java/android/media/soundtrigger_middleware/RecognitionMode.aidl +++ b/media/aidl/android/media/soundtrigger_middleware/RecognitionMode.aidl diff --git a/media/java/android/media/soundtrigger_middleware/RecognitionStatus.aidl b/media/aidl/android/media/soundtrigger_middleware/RecognitionStatus.aidl index d563edca547d..d563edca547d 100644 --- a/media/java/android/media/soundtrigger_middleware/RecognitionStatus.aidl +++ b/media/aidl/android/media/soundtrigger_middleware/RecognitionStatus.aidl diff --git a/media/java/android/media/soundtrigger_middleware/SoundModel.aidl b/media/aidl/android/media/soundtrigger_middleware/SoundModel.aidl index 81d8291e85aa..cee3635a1e3b 100644 --- a/media/java/android/media/soundtrigger_middleware/SoundModel.aidl +++ b/media/aidl/android/media/soundtrigger_middleware/SoundModel.aidl @@ -16,6 +16,7 @@ package android.media.soundtrigger_middleware; import android.media.soundtrigger_middleware.SoundModelType; +import android.os.ParcelFileDescriptor; /** * Base sound model descriptor. This struct can be extended for various specific types by way of @@ -32,7 +33,7 @@ parcelable SoundModel { * was build for */ String vendorUuid; /** Opaque data transparent to Android framework */ - FileDescriptor data; + ParcelFileDescriptor data; /** Size of the above data, in bytes. */ int dataSize; } diff --git a/media/java/android/media/soundtrigger_middleware/SoundModelType.aidl b/media/aidl/android/media/soundtrigger_middleware/SoundModelType.aidl index f2abc9af7780..f2abc9af7780 100644 --- a/media/java/android/media/soundtrigger_middleware/SoundModelType.aidl +++ b/media/aidl/android/media/soundtrigger_middleware/SoundModelType.aidl diff --git a/media/java/android/media/soundtrigger_middleware/SoundTriggerModuleDescriptor.aidl b/media/aidl/android/media/soundtrigger_middleware/SoundTriggerModuleDescriptor.aidl index 667135ff61b9..667135ff61b9 100644 --- a/media/java/android/media/soundtrigger_middleware/SoundTriggerModuleDescriptor.aidl +++ b/media/aidl/android/media/soundtrigger_middleware/SoundTriggerModuleDescriptor.aidl diff --git a/media/java/android/media/soundtrigger_middleware/SoundTriggerModuleProperties.aidl b/media/aidl/android/media/soundtrigger_middleware/SoundTriggerModuleProperties.aidl index 9c56e7b98b3f..9c56e7b98b3f 100644 --- a/media/java/android/media/soundtrigger_middleware/SoundTriggerModuleProperties.aidl +++ b/media/aidl/android/media/soundtrigger_middleware/SoundTriggerModuleProperties.aidl diff --git a/media/java/android/media/soundtrigger_middleware/Status.aidl b/media/aidl/android/media/soundtrigger_middleware/Status.aidl index c7623f5bf491..c7623f5bf491 100644 --- a/media/java/android/media/soundtrigger_middleware/Status.aidl +++ b/media/aidl/android/media/soundtrigger_middleware/Status.aidl diff --git a/media/java/android/media/AudioAttributes.aidl b/media/java/android/media/AudioAttributes.aidl index 04587f9d48d4..88a6f50d2d3c 100644 --- a/media/java/android/media/AudioAttributes.aidl +++ b/media/java/android/media/AudioAttributes.aidl @@ -15,4 +15,4 @@ package android.media; -parcelable AudioAttributes; +@JavaOnlyStableParcelable parcelable AudioAttributes; diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java index e1e55c25b3fa..22b5ca53e7e9 100644 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -96,8 +96,8 @@ public class AudioManager { private Context mOriginalContext; private Context mApplicationContext; private long mVolumeKeyUpTime; - private final boolean mUseVolumeKeySounds; - private final boolean mUseFixedVolume; + private boolean mUseFixedVolumeInitialized; + private boolean mUseFixedVolume; private static final String TAG = "AudioManager"; private static final boolean DEBUG = false; private static final AudioPortEventHandler sAudioPortEventHandler = new AudioPortEventHandler(); @@ -711,8 +711,6 @@ public class AudioManager { */ @UnsupportedAppUsage public AudioManager() { - mUseVolumeKeySounds = true; - mUseFixedVolume = false; } /** @@ -721,10 +719,6 @@ public class AudioManager { @UnsupportedAppUsage public AudioManager(Context context) { setContext(context); - mUseVolumeKeySounds = getContext().getResources().getBoolean( - com.android.internal.R.bool.config_useVolumeKeySounds); - mUseFixedVolume = getContext().getResources().getBoolean( - com.android.internal.R.bool.config_useFixedVolume); } private Context getContext() { @@ -823,6 +817,18 @@ public class AudioManager { * </ul> */ public boolean isVolumeFixed() { + synchronized (this) { + try { + if (!mUseFixedVolumeInitialized) { + mUseFixedVolume = getContext().getResources().getBoolean( + com.android.internal.R.bool.config_useFixedVolume); + } + } catch (Exception e) { + } finally { + // only ever try once, so always consider initialized even if query failed + mUseFixedVolumeInitialized = true; + } + } return mUseFixedVolume; } diff --git a/media/java/android/media/ExifInterface.java b/media/java/android/media/ExifInterface.java index ddc7db771550..8845d6954db2 100644 --- a/media/java/android/media/ExifInterface.java +++ b/media/java/android/media/ExifInterface.java @@ -31,7 +31,6 @@ import android.compat.annotation.UnsupportedAppUsage; import android.content.res.AssetManager; import android.graphics.Bitmap; import android.graphics.BitmapFactory; -import android.os.Build; import android.system.ErrnoException; import android.system.Os; import android.system.OsConstants; @@ -599,7 +598,6 @@ public class ExifInterface { private static final int WEBP_CHUNK_TYPE_BYTE_LENGTH = 4; private static final int WEBP_CHUNK_SIZE_BYTE_LENGTH = 4; - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) @GuardedBy("sFormatter") private static SimpleDateFormat sFormatter; @GuardedBy("sFormatterTz") @@ -1446,18 +1444,17 @@ public class ExifInterface { sExifPointerTagMap.put(EXIF_POINTER_TAGS[5].number, IFD_TYPE_ORF_IMAGE_PROCESSING); // 8256 } - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) private String mFilename; private FileDescriptor mSeekableFileDescriptor; private AssetManager.AssetInputStream mAssetInputStream; private boolean mIsInputStream; private int mMimeType; private boolean mIsExifDataOnly; - @UnsupportedAppUsage + @UnsupportedAppUsage(publicAlternatives = "Use {@link #getAttribute(java.lang.String)} " + + "instead.") private final HashMap[] mAttributes = new HashMap[EXIF_TAGS.length]; private Set<Integer> mHandledIfdOffsets = new HashSet<>(EXIF_TAGS.length); private ByteOrder mExifByteOrder = ByteOrder.BIG_ENDIAN; - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) private boolean mHasThumbnail; private boolean mHasThumbnailStrips; private boolean mAreThumbnailStripsConsecutive; @@ -2409,11 +2406,8 @@ public class ExifInterface { } /** - * Returns parsed {@code DateTime} value, or -1 if unavailable or invalid. - * - * @hide + * Returns parsed {@link #TAG_DATETIME} value, or -1 if unavailable or invalid. */ - @UnsupportedAppUsage public @CurrentTimeMillisLong long getDateTime() { return parseDateTime(getAttribute(TAG_DATETIME), getAttribute(TAG_SUBSEC_TIME), @@ -2421,10 +2415,7 @@ public class ExifInterface { } /** - * Returns parsed {@code DateTimeDigitized} value, or -1 if unavailable or - * invalid. - * - * @hide + * Returns parsed {@link #TAG_DATETIME_DIGITIZED} value, or -1 if unavailable or invalid. */ public @CurrentTimeMillisLong long getDateTimeDigitized() { return parseDateTime(getAttribute(TAG_DATETIME_DIGITIZED), @@ -2433,12 +2424,8 @@ public class ExifInterface { } /** - * Returns parsed {@code DateTimeOriginal} value, or -1 if unavailable or - * invalid. - * - * @hide + * Returns parsed {@link #TAG_DATETIME_ORIGINAL} value, or -1 if unavailable or invalid. */ - @UnsupportedAppUsage public @CurrentTimeMillisLong long getDateTimeOriginal() { return parseDateTime(getAttribute(TAG_DATETIME_ORIGINAL), getAttribute(TAG_SUBSEC_TIME_ORIGINAL), @@ -2490,9 +2477,7 @@ public class ExifInterface { /** * Returns number of milliseconds since Jan. 1, 1970, midnight UTC. * Returns -1 if the date time information if not available. - * @hide */ - @UnsupportedAppUsage public long getGpsDateTime() { String date = getAttribute(TAG_GPS_DATESTAMP); String time = getAttribute(TAG_GPS_TIMESTAMP); @@ -2518,7 +2503,6 @@ public class ExifInterface { } /** {@hide} */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) public static float convertRationalLatLonToFloat(String rationalString, String ref) { try { String [] parts = rationalString.split(","); diff --git a/media/java/android/media/MediaMetadata.aidl b/media/java/android/media/MediaMetadata.aidl index 66ee48304168..1d78da2a1b8e 100644 --- a/media/java/android/media/MediaMetadata.aidl +++ b/media/java/android/media/MediaMetadata.aidl @@ -15,4 +15,4 @@ package android.media; -parcelable MediaMetadata; +@JavaOnlyStableParcelable parcelable MediaMetadata; diff --git a/media/java/android/media/MediaTranscodeManager.java b/media/java/android/media/MediaTranscodeManager.java index e09241137541..21376bb0fecb 100644 --- a/media/java/android/media/MediaTranscodeManager.java +++ b/media/java/android/media/MediaTranscodeManager.java @@ -83,13 +83,13 @@ import java.util.concurrent.Executors; <pre class=prettyprint> TranscodingRequest request = - new TranscodingRequest.Builder() - .setSourceUri(srcUri) - .setDestinationUri(dstUri) - .setType(MediaTranscodeManager.TRANSCODING_TYPE_VIDEO) - .setPriority(REALTIME) - .setVideoTrackFormat(videoFormat) - .build(); + new TranscodingRequest.Builder() + .setSourceUri(srcUri) + .setDestinationUri(dstUri) + .setType(MediaTranscodeManager.TRANSCODING_TYPE_VIDEO) + .setPriority(REALTIME) + .setVideoTrackFormat(videoFormat) + .build(); }</pre> TODO(hkuang): Add architecture diagram showing the transcoding service and api. @@ -498,6 +498,20 @@ public final class MediaTranscodeManager { /** Uri of the destination media file. */ private @NonNull Uri mDestinationUri; + /** + * The UID of the client that the TranscodingRequest is for. Only privileged caller could + * set this Uid as only they could do the transcoding on behalf of the client. + * -1 means not available. + */ + private int mClientUid = -1; + + /** + * The Pid of the client that the TranscodingRequest is for. Only privileged caller could + * set this Uid as only they could do the transcoding on behalf of the client. + * -1 means not available. + */ + private int mClientPid = -1; + /** Type of the transcoding. */ private @TranscodingType int mType = TRANSCODING_TYPE_UNKNOWN; @@ -534,6 +548,8 @@ public final class MediaTranscodeManager { private TranscodingRequest(Builder b) { mSourceUri = b.mSourceUri; mDestinationUri = b.mDestinationUri; + mClientUid = b.mClientUid; + mClientPid = b.mClientPid; mPriority = b.mPriority; mType = b.mType; mVideoTrackFormat = b.mVideoTrackFormat; @@ -554,6 +570,16 @@ public final class MediaTranscodeManager { return mSourceUri; } + /** Return the UID of the client that this request is for. -1 means not available. */ + public int getClientUid() { + return mClientUid; + } + + /** Return the PID of the client that this request is for. -1 means not available. */ + public int getClientPid() { + return mClientPid; + } + /** Return destination uri of the transcoding. */ @NonNull public Uri getDestinationUri() { @@ -592,6 +618,8 @@ public final class MediaTranscodeManager { parcel.transcodingType = mType; parcel.sourceFilePath = mSourceUri.toString(); parcel.destinationFilePath = mDestinationUri.toString(); + parcel.clientUid = mClientUid; + parcel.clientPid = mClientPid; parcel.requestedVideoTrackFormat = convertToVideoTrackFormat(mVideoTrackFormat); if (mTestConfig != null) { parcel.isForTesting = true; @@ -667,6 +695,8 @@ public final class MediaTranscodeManager { public static final class Builder { private @NonNull Uri mSourceUri; private @NonNull Uri mDestinationUri; + private int mClientUid = -1; + private int mClientPid = -1; private @TranscodingType int mType = TRANSCODING_TYPE_UNKNOWN; private @TranscodingPriority int mPriority = PRIORITY_UNKNOWN; private @Nullable MediaFormat mVideoTrackFormat; @@ -710,6 +740,38 @@ public final class MediaTranscodeManager { } /** + * Specify the UID of the client that this request is for. + * @param uid client Uid. + * @return The same builder instance. + * @throws IllegalArgumentException if uid is invalid. + * TODO(hkuang): Check the permission if it is allowed. + */ + @NonNull + public Builder setClientUid(int uid) { + if (uid <= 0) { + throw new IllegalArgumentException("Invalid Uid"); + } + mClientUid = uid; + return this; + } + + /** + * Specify the PID of the client that this request is for. + * @param pid client Pid. + * @return The same builder instance. + * @throws IllegalArgumentException if pid is invalid. + * TODO(hkuang): Check the permission if it is allowed. + */ + @NonNull + public Builder setClientPid(int pid) { + if (pid <= 0) { + throw new IllegalArgumentException("Invalid pid"); + } + mClientPid = pid; + return this; + } + + /** * Specifies the priority of the transcoding. * * @param priority Must be one of the {@code PRIORITY_*} @@ -1225,6 +1287,50 @@ public final class MediaTranscodeManager { } } + @Override + public String toString() { + String result; + String status; + + switch (mResult) { + case RESULT_NONE: + result = "RESULT_NONE"; + break; + case RESULT_SUCCESS: + result = "RESULT_SUCCESS"; + break; + case RESULT_ERROR: + result = "RESULT_ERROR"; + break; + case RESULT_CANCELED: + result = "RESULT_CANCELED"; + break; + default: + result = String.valueOf(mResult); + break; + } + + switch (mStatus) { + case STATUS_PENDING: + status = "STATUS_PENDING"; + break; + case STATUS_PAUSED: + status = "STATUS_PAUSED"; + break; + case STATUS_RUNNING: + status = "STATUS_RUNNING"; + break; + case STATUS_FINISHED: + status = "STATUS_FINISHED"; + break; + default: + status = String.valueOf(mStatus); + break; + } + return String.format(" Job: {id: %d, status: %s, result: %s, progress: %d}", + mJobId, status, result, mProgress); + } + private void updateProgress(int newProgress) { synchronized (mLock) { mProgress = newProgress; @@ -1275,6 +1381,8 @@ public final class MediaTranscodeManager { // Converts the request to TranscodingRequestParcel. TranscodingRequestParcel requestParcel = transcodingRequest.writeToParcel(); + Log.i(TAG, "Getting transcoding request " + transcodingRequest.getSourceUri()); + // Submits the request to MediaTranscoding service. try { TranscodingJobParcel jobParcel = new TranscodingJobParcel(); diff --git a/media/java/android/media/RemoteController.java b/media/java/android/media/RemoteController.java index 9e48f1e05391..35cfaca8562a 100644 --- a/media/java/android/media/RemoteController.java +++ b/media/java/android/media/RemoteController.java @@ -538,7 +538,7 @@ import java.util.List; handler = new Handler(Looper.getMainLooper()); } mSessionManager.addOnActiveSessionsChangedListener(mSessionListener, listenerComponent, - UserHandle.myUserId(), handler); + handler); mSessionListener.onActiveSessionsChanged(mSessionManager .getActiveSessions(listenerComponent)); if (DEBUG) { diff --git a/media/java/android/media/session/MediaSessionManager.java b/media/java/android/media/session/MediaSessionManager.java index 9494295e4bd9..3acb9516054e 100644 --- a/media/java/android/media/session/MediaSessionManager.java +++ b/media/java/android/media/session/MediaSessionManager.java @@ -20,6 +20,7 @@ import android.annotation.CallbackExecutor; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; +import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.annotation.SystemService; import android.compat.annotation.UnsupportedAppUsage; @@ -262,18 +263,14 @@ public final class MediaSessionManager { } /** - * Add a listener to be notified when the list of active sessions - * changes.This requires the - * android.Manifest.permission.MEDIA_CONTENT_CONTROL permission be held by - * the calling app. You may also retrieve this list if your app is an - * enabled notification listener using the - * {@link NotificationListenerService} APIs, in which case you must pass the - * {@link ComponentName} of your enabled listener. Updates will be posted to - * the thread that registered the listener. + * Add a listener to be notified when the list of active sessions changes. This requires the + * {@link android.Manifest.permission#MEDIA_CONTENT_CONTROL} permission be held by the calling + * app. You may also retrieve this list if your app is an enabled notification listener using + * the {@link NotificationListenerService} APIs, in which case you must pass the + * {@link ComponentName} of your enabled listener. * * @param sessionListener The listener to add. - * @param notificationListener The enabled notification listener component. - * May be null. + * @param notificationListener The enabled notification listener component. May be null. */ public void addOnActiveSessionsChangedListener( @NonNull OnActiveSessionsChangedListener sessionListener, @@ -282,18 +279,15 @@ public final class MediaSessionManager { } /** - * Add a listener to be notified when the list of active sessions - * changes.This requires the - * android.Manifest.permission.MEDIA_CONTENT_CONTROL permission be held by - * the calling app. You may also retrieve this list if your app is an - * enabled notification listener using the - * {@link NotificationListenerService} APIs, in which case you must pass the - * {@link ComponentName} of your enabled listener. Updates will be posted to - * the handler specified or to the caller's thread if the handler is null. + * Add a listener to be notified when the list of active sessions changes. This requires the + * {@link android.Manifest.permission#MEDIA_CONTENT_CONTROL} permission be held by the calling + * app. You may also retrieve this list if your app is an enabled notification listener using + * the {@link NotificationListenerService} APIs, in which case you must pass the + * {@link ComponentName} of your enabled listener. Updates will be posted to the handler + * specified or to the caller's thread if the handler is null. * * @param sessionListener The listener to add. - * @param notificationListener The enabled notification listener component. - * May be null. + * @param notificationListener The enabled notification listener component. May be null. * @param handler The handler to post events to. */ public void addOnActiveSessionsChangedListener( @@ -304,21 +298,24 @@ public final class MediaSessionManager { } /** - * Add a listener to be notified when the list of active sessions - * changes.This requires the - * android.Manifest.permission.MEDIA_CONTENT_CONTROL permission be held by - * the calling app. You may also retrieve this list if your app is an - * enabled notification listener using the - * {@link NotificationListenerService} APIs, in which case you must pass the - * {@link ComponentName} of your enabled listener. + * Add a listener to be notified when the list of active sessions changes for the given user. + * The calling app must have the {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL} + * permission if it wants to call this method for a user that is not running the app. + * <p> + * This requires the {@link android.Manifest.permission#MEDIA_CONTENT_CONTROL} permission be + * held by the calling app. You may also retrieve this list if your app is an enabled + * notification listener using the {@link NotificationListenerService} APIs, in which case you + * must pass the {@link ComponentName} of your enabled listener. Updates will be posted to the + * handler specified or to the caller's thread if the handler is null. * * @param sessionListener The listener to add. - * @param notificationListener The enabled notification listener component. - * May be null. + * @param notificationListener The enabled notification listener component. May be null. * @param userId The userId to listen for changes on. * @param handler The handler to post updates on. * @hide */ + @SuppressLint({"ExecutorRegistration", "SamShouldBeLast"}) + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) public void addOnActiveSessionsChangedListener( @NonNull OnActiveSessionsChangedListener sessionListener, @Nullable ComponentName notificationListener, int userId, @Nullable Handler handler) { diff --git a/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodeManagerTest.java b/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodeManagerTest.java index 33d6d64c7f37..21ed840c7313 100644 --- a/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodeManagerTest.java +++ b/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodeManagerTest.java @@ -203,6 +203,42 @@ public class MediaTranscodeManagerTest } /** + * Verify that setting invalid pid will throw exception. + */ + @Test + public void testCreateTranscodingWithInvalidClientPid() throws Exception { + assertThrows(IllegalArgumentException.class, () -> { + TranscodingRequest request = + new TranscodingRequest.Builder() + .setSourceUri(mSourceHEVCVideoUri) + .setDestinationUri(mDestinationUri) + .setType(MediaTranscodeManager.TRANSCODING_TYPE_VIDEO) + .setPriority(MediaTranscodeManager.PRIORITY_REALTIME) + .setClientPid(-1) + .setVideoTrackFormat(createMediaFormat()) + .build(); + }); + } + + /** + * Verify that setting invalid uid will throw exception. + */ + @Test + public void testCreateTranscodingWithInvalidClientUid() throws Exception { + assertThrows(IllegalArgumentException.class, () -> { + TranscodingRequest request = + new TranscodingRequest.Builder() + .setSourceUri(mSourceHEVCVideoUri) + .setDestinationUri(mDestinationUri) + .setType(MediaTranscodeManager.TRANSCODING_TYPE_VIDEO) + .setPriority(MediaTranscodeManager.PRIORITY_REALTIME) + .setClientUid(-1) + .setVideoTrackFormat(createMediaFormat()) + .build(); + }); + } + + /** * Verify that setting null source uri will throw exception. */ @Test @@ -425,16 +461,24 @@ public class MediaTranscodeManagerTest MediaFormat videoTrackFormat = resolver.resolveVideoFormat(); assertNotNull(videoTrackFormat); + int pid = android.os.Process.myPid(); + int uid = android.os.Process.myUid(); + TranscodingRequest request = new TranscodingRequest.Builder() .setSourceUri(mSourceHEVCVideoUri) .setDestinationUri(destinationUri) .setType(MediaTranscodeManager.TRANSCODING_TYPE_VIDEO) + .setClientPid(pid) + .setClientUid(uid) .setPriority(MediaTranscodeManager.PRIORITY_REALTIME) .setVideoTrackFormat(videoTrackFormat) .build(); Executor listenerExecutor = Executors.newSingleThreadExecutor(); + assertEquals(pid, request.getClientPid()); + assertEquals(uid, request.getClientUid()); + Log.i(TAG, "transcoding to " + videoTrackFormat); TranscodingJob job = mMediaTranscodeManager.enqueueRequest(request, listenerExecutor, diff --git a/non-updatable-api/Android.bp b/non-updatable-api/Android.bp new file mode 100644 index 000000000000..4037781c1844 --- /dev/null +++ b/non-updatable-api/Android.bp @@ -0,0 +1,35 @@ +// 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 { + default_visibility: ["//visibility:private"], +} + +filegroup { + name: "non-updatable-current.txt", + srcs: ["current.txt"], + visibility: ["//frameworks/base/api"], +} + +filegroup { + name: "non-updatable-system-current.txt", + srcs: ["system-current.txt"], + visibility: ["//frameworks/base/api"], +} + +filegroup { + name: "non-updatable-module-lib-current.txt", + srcs: ["module-lib-current.txt"], + visibility: ["//frameworks/base/api"], +} diff --git a/non-updatable-api/current.txt b/non-updatable-api/current.txt index 80300611c7f2..2d5d48f3f09b 100644 --- a/non-updatable-api/current.txt +++ b/non-updatable-api/current.txt @@ -6138,6 +6138,7 @@ package android.app { field @NonNull public static final android.os.Parcelable.Creator<android.app.PendingIntent> CREATOR; field public static final int FLAG_CANCEL_CURRENT = 268435456; // 0x10000000 field public static final int FLAG_IMMUTABLE = 67108864; // 0x4000000 + field public static final int FLAG_MUTABLE = 33554432; // 0x2000000 field public static final int FLAG_NO_CREATE = 536870912; // 0x20000000 field public static final int FLAG_ONE_SHOT = 1073741824; // 0x40000000 field public static final int FLAG_UPDATE_CURRENT = 134217728; // 0x8000000 @@ -14292,6 +14293,7 @@ package android.graphics { public final class BlurShader extends android.graphics.Shader { ctor public BlurShader(float, float, @Nullable android.graphics.Shader); + ctor public BlurShader(float, float, @Nullable android.graphics.Shader, @NonNull android.graphics.Shader.TileMode); } public class Camera { @@ -15177,6 +15179,20 @@ package android.graphics { ctor public PaintFlagsDrawFilter(int, int); } + public final class ParcelableColorSpace extends android.graphics.ColorSpace implements android.os.Parcelable { + ctor public ParcelableColorSpace(@NonNull android.graphics.ColorSpace); + method public int describeContents(); + method @NonNull public float[] fromXyz(@NonNull float[]); + method @NonNull public android.graphics.ColorSpace getColorSpace(); + method public float getMaxValue(int); + method public float getMinValue(int); + method public static boolean isParcelable(@NonNull android.graphics.ColorSpace); + method public boolean isWideGamut(); + method @NonNull public float[] toXyz(@NonNull float[]); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.graphics.ParcelableColorSpace> CREATOR; + } + public class Path { ctor public Path(); ctor public Path(@Nullable android.graphics.Path); @@ -15612,6 +15628,7 @@ package android.graphics { public enum Shader.TileMode { enum_constant public static final android.graphics.Shader.TileMode CLAMP; + enum_constant public static final android.graphics.Shader.TileMode DECAL; enum_constant public static final android.graphics.Shader.TileMode MIRROR; enum_constant public static final android.graphics.Shader.TileMode REPEAT; } @@ -24895,6 +24912,10 @@ package android.media { method public double getAttributeDouble(@NonNull String, double); method public int getAttributeInt(@NonNull String, int); method @Nullable public long[] getAttributeRange(@NonNull String); + method public long getDateTime(); + method public long getDateTimeDigitized(); + method public long getDateTimeOriginal(); + method public long getGpsDateTime(); method public boolean getLatLong(float[]); method public byte[] getThumbnail(); method public android.graphics.Bitmap getThumbnailBitmap(); diff --git a/non-updatable-api/module-lib-current.txt b/non-updatable-api/module-lib-current.txt index 3810325c982f..85136df0d1ad 100644 --- a/non-updatable-api/module-lib-current.txt +++ b/non-updatable-api/module-lib-current.txt @@ -66,6 +66,7 @@ package android.media.session { } public final class MediaSessionManager { + method public void addOnActiveSessionsChangedListener(@NonNull android.media.session.MediaSessionManager.OnActiveSessionsChangedListener, @Nullable android.content.ComponentName, int, @Nullable android.os.Handler); method public void dispatchMediaKeyEventAsSystemService(@NonNull android.view.KeyEvent); method public boolean dispatchMediaKeyEventAsSystemService(@NonNull android.media.session.MediaSession.Token, @NonNull android.view.KeyEvent); method public void dispatchVolumeKeyEventAsSystemService(@NonNull android.view.KeyEvent, int); diff --git a/non-updatable-api/system-current.txt b/non-updatable-api/system-current.txt index 10fe058bb76f..e27ca09f8e86 100644 --- a/non-updatable-api/system-current.txt +++ b/non-updatable-api/system-current.txt @@ -4339,6 +4339,8 @@ package android.media { } public static final class MediaTranscodeManager.TranscodingRequest { + method public int getClientPid(); + method public int getClientUid(); method @NonNull public android.net.Uri getDestinationUri(); method public int getPriority(); method @NonNull public android.net.Uri getSourceUri(); @@ -4349,6 +4351,8 @@ package android.media { public static final class MediaTranscodeManager.TranscodingRequest.Builder { ctor public MediaTranscodeManager.TranscodingRequest.Builder(); method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest build(); + method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setClientPid(int); + method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setClientUid(int); method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setDestinationUri(@NonNull android.net.Uri); method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setPriority(int); method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setSourceUri(@NonNull android.net.Uri); diff --git a/packages/CarSystemUI/res/layout/sysui_overlay_window.xml b/packages/CarSystemUI/res/layout/sysui_overlay_window.xml index 2c9788955bfa..e7295aa6383d 100644 --- a/packages/CarSystemUI/res/layout/sysui_overlay_window.xml +++ b/packages/CarSystemUI/res/layout/sysui_overlay_window.xml @@ -25,7 +25,8 @@ <ViewStub android:id="@+id/notification_panel_stub" android:layout_width="match_parent" android:layout_height="match_parent" - android:layout="@layout/notification_panel_container"/> + android:layout="@layout/notification_panel_container" + android:layout_marginBottom="@dimen/car_bottom_navigation_bar_height"/> <ViewStub android:id="@+id/keyguard_stub" android:layout_width="match_parent" diff --git a/packages/CarSystemUI/res/values/dimens.xml b/packages/CarSystemUI/res/values/dimens.xml index 28b8eadf9750..fe060ac8e707 100644 --- a/packages/CarSystemUI/res/values/dimens.xml +++ b/packages/CarSystemUI/res/values/dimens.xml @@ -207,6 +207,12 @@ <dimen name="car_navigation_bar_width">760dp</dimen> <dimen name="car_left_navigation_bar_width">96dp</dimen> <dimen name="car_right_navigation_bar_width">96dp</dimen> + <!-- In order to change the height of the bottom nav bar, overlay navigation_bar_height in + frameworks/base/core/res/res instead. --> + <dimen name="car_bottom_navigation_bar_height">@*android:dimen/navigation_bar_height</dimen> + <!-- In order to change the height of the top nav bar, overlay status_bar_height in + frameworks/base/core/res/res instead. --> + <dimen name="car_top_navigation_bar_height">@*android:dimen/status_bar_height</dimen> <dimen name="car_user_switcher_container_height">420dp</dimen> <!-- This must be the negative of car_user_switcher_container_height for the animation. --> diff --git a/packages/CarSystemUI/res/values/strings.xml b/packages/CarSystemUI/res/values/strings.xml index fbdb5167fade..06ae7cfd6d1b 100644 --- a/packages/CarSystemUI/res/values/strings.xml +++ b/packages/CarSystemUI/res/values/strings.xml @@ -22,6 +22,8 @@ <string name="hvac_min_text">Min</string> <!-- String to represent largest setting of an HVAC system [CHAR LIMIT=10]--> <string name="hvac_max_text">Max</string> + <!-- String to display when no HVAC temperature is available --> + <string name="hvac_null_temp_text" translatable="false">--</string> <!-- Text for voice recognition toast. [CHAR LIMIT=60] --> <string name="voice_recognition_toast">Voice recognition now handled by connected Bluetooth device</string> <!-- Name of Guest Profile. [CHAR LIMIT=35] --> diff --git a/packages/CarSystemUI/src/com/android/systemui/car/hvac/AdjustableTemperatureView.java b/packages/CarSystemUI/src/com/android/systemui/car/hvac/AdjustableTemperatureView.java index 85d4ceb81eeb..af2a1d36bbd7 100644 --- a/packages/CarSystemUI/src/com/android/systemui/car/hvac/AdjustableTemperatureView.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/hvac/AdjustableTemperatureView.java @@ -40,6 +40,9 @@ public class AdjustableTemperatureView extends LinearLayout implements Temperatu private float mMinTempC; private float mMaxTempC; private String mTempFormat; + private String mNullTempText; + private String mMinTempText; + private String mMaxTempText; private boolean mDisplayInFahrenheit = false; private HvacController mHvacController; @@ -59,6 +62,9 @@ public class AdjustableTemperatureView extends LinearLayout implements Temperatu mTempFormat = getResources().getString(R.string.hvac_temperature_format); mMinTempC = getResources().getFloat(R.dimen.hvac_min_value_celsius); mMaxTempC = getResources().getFloat(R.dimen.hvac_max_value_celsius); + mNullTempText = getResources().getString(R.string.hvac_null_temp_text); + mMinTempText = getResources().getString(R.string.hvac_min_text); + mMaxTempText = getResources().getString(R.string.hvac_max_text); initializeButtons(); } @@ -69,12 +75,23 @@ public class AdjustableTemperatureView extends LinearLayout implements Temperatu @Override public void setTemp(float tempC) { - if (tempC > mMaxTempC || tempC < mMinTempC) { - return; - } if (mTempTextView == null) { mTempTextView = findViewById(R.id.hvac_temperature_text); } + if (Float.isNaN(tempC)) { + mTempTextView.setText(mNullTempText); + return; + } + if (tempC <= mMinTempC) { + mTempTextView.setText(mMinTempText); + mCurrentTempC = mMinTempC; + return; + } + if (tempC >= mMaxTempC) { + mTempTextView.setText(mMaxTempText); + mCurrentTempC = mMaxTempC; + return; + } mTempTextView.setText(String.format(mTempFormat, mDisplayInFahrenheit ? convertToFahrenheit(tempC) : tempC)); mCurrentTempC = tempC; diff --git a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/SystemBarConfigs.java b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/SystemBarConfigs.java index 078196e18b88..dfda4e6b2635 100644 --- a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/SystemBarConfigs.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/SystemBarConfigs.java @@ -216,7 +216,7 @@ public class SystemBarConfigs { new SystemBarConfigBuilder() .setSide(TOP) .setGirth(mResources.getDimensionPixelSize( - com.android.internal.R.dimen.status_bar_height)) + R.dimen.car_top_navigation_bar_height)) .setBarType(mResources.getInteger(R.integer.config_topSystemBarType)) .setZOrder(mResources.getInteger(R.integer.config_topSystemBarZOrder)) .setHideForKeyboard(mResources.getBoolean( @@ -230,7 +230,7 @@ public class SystemBarConfigs { new SystemBarConfigBuilder() .setSide(BOTTOM) .setGirth(mResources.getDimensionPixelSize( - com.android.internal.R.dimen.navigation_bar_height)) + R.dimen.car_bottom_navigation_bar_height)) .setBarType(mResources.getInteger(R.integer.config_bottomSystemBarType)) .setZOrder( mResources.getInteger(R.integer.config_bottomSystemBarZOrder)) diff --git a/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java b/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java index c7155f4b21d2..e8b332149864 100644 --- a/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java @@ -16,8 +16,6 @@ package com.android.systemui.car.notification; -import static android.view.WindowInsets.Type.navigationBars; - import android.app.ActivityManager; import android.car.Car; import android.car.drivingstate.CarUxRestrictionsManager; @@ -25,6 +23,8 @@ import android.content.Context; import android.content.res.Resources; import android.graphics.Rect; import android.graphics.drawable.Drawable; +import android.inputmethodservice.InputMethodService; +import android.os.IBinder; import android.os.RemoteException; import android.util.Log; import android.view.GestureDetector; @@ -82,6 +82,7 @@ public class NotificationPanelViewController extends OverlayPanelViewController private final StatusBarStateController mStatusBarStateController; private final boolean mEnableHeadsUpNotificationWhenNotificationShadeOpen; private final NotificationVisibilityLogger mNotificationVisibilityLogger; + private final int mNavBarHeight; private float mInitialBackgroundAlpha; private float mBackgroundAlphaDiff; @@ -138,7 +139,10 @@ public class NotificationPanelViewController extends OverlayPanelViewController mStatusBarStateController = statusBarStateController; mNotificationVisibilityLogger = notificationVisibilityLogger; + mNavBarHeight = mResources.getDimensionPixelSize(R.dimen.car_bottom_navigation_bar_height); + mCommandQueue.addCallback(this); + // Notification background setup. mInitialBackgroundAlpha = (float) mResources.getInteger( R.integer.config_initialNotificationBackgroundAlpha) / 100; @@ -179,6 +183,21 @@ public class NotificationPanelViewController extends OverlayPanelViewController } } + @Override + public void setImeWindowStatus(int displayId, IBinder token, int vis, int backDisposition, + boolean showImeSwitcher) { + if (mContext.getDisplayId() != displayId) { + return; + } + boolean isKeyboardVisible = (vis & InputMethodService.IME_VISIBLE) != 0; + int bottomMargin = isKeyboardVisible ? 0 : mNavBarHeight; + ViewGroup container = (ViewGroup) getLayout(); + ViewGroup.MarginLayoutParams params = + (ViewGroup.MarginLayoutParams) container.getLayoutParams(); + params.setMargins(params.leftMargin, params.topMargin, params.rightMargin, bottomMargin); + container.setLayoutParams(params); + } + // OverlayViewController @Override @@ -204,7 +223,7 @@ public class NotificationPanelViewController extends OverlayPanelViewController @Override protected int getInsetTypesToFit() { - return navigationBars(); + return 0; } @Override diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/hvac/AdjustableTemperatureViewTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/hvac/AdjustableTemperatureViewTest.java index a3a55aae5f18..fe071d54fb10 100644 --- a/packages/CarSystemUI/tests/src/com/android/systemui/car/hvac/AdjustableTemperatureViewTest.java +++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/hvac/AdjustableTemperatureViewTest.java @@ -98,6 +98,48 @@ public class AdjustableTemperatureViewTest extends SysuiTestCase { } @Test + public void setTemp_tempNaN_setsTextToNaNText() { + when(mCarPropertyManager.isPropertyAvailable(eq(HVAC_TEMPERATURE_SET), + anyInt())).thenReturn(true); + when(mCarPropertyManager.getFloatProperty(eq(HVAC_TEMPERATURE_SET), anyInt())).thenReturn( + Float.NaN); + + mHvacController.addTemperatureViewToController(mAdjustableTemperatureView); + + TextView tempText = mAdjustableTemperatureView.findViewById(R.id.hvac_temperature_text); + assertEquals(tempText.getText(), + getContext().getResources().getString(R.string.hvac_null_temp_text)); + } + + @Test + public void setTemp_tempBelowMin_setsTextToMinTempText() { + when(mCarPropertyManager.isPropertyAvailable(eq(HVAC_TEMPERATURE_SET), + anyInt())).thenReturn(true); + when(mCarPropertyManager.getFloatProperty(eq(HVAC_TEMPERATURE_SET), anyInt())).thenReturn( + getContext().getResources().getFloat(R.dimen.hvac_min_value_celsius)); + + mHvacController.addTemperatureViewToController(mAdjustableTemperatureView); + + TextView tempText = mAdjustableTemperatureView.findViewById(R.id.hvac_temperature_text); + assertEquals(tempText.getText(), + getContext().getResources().getString(R.string.hvac_min_text)); + } + + @Test + public void setTemp_tempAboveMax_setsTextToMaxTempText() { + when(mCarPropertyManager.isPropertyAvailable(eq(HVAC_TEMPERATURE_SET), + anyInt())).thenReturn(true); + when(mCarPropertyManager.getFloatProperty(eq(HVAC_TEMPERATURE_SET), anyInt())).thenReturn( + getContext().getResources().getFloat(R.dimen.hvac_max_value_celsius)); + + mHvacController.addTemperatureViewToController(mAdjustableTemperatureView); + + TextView tempText = mAdjustableTemperatureView.findViewById(R.id.hvac_temperature_text); + assertEquals(tempText.getText(), + getContext().getResources().getString(R.string.hvac_max_text)); + } + + @Test public void setTemperatureToFahrenheit_callsViewSetDisplayInFahrenheit() { when(mCarPropertyManager.isPropertyAvailable(eq(HVAC_TEMPERATURE_SET), anyInt())).thenReturn(true); diff --git a/packages/InputDevices/res/values-af/strings.xml b/packages/InputDevices/res/values-af/strings.xml index 4bc7bf6e5d1f..462a6a9bb7ac 100644 --- a/packages/InputDevices/res/values-af/strings.xml +++ b/packages/InputDevices/res/values-af/strings.xml @@ -19,8 +19,7 @@ <string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Switserse Duits"</string> <string name="keyboard_layout_belgian" msgid="2011984572838651558">"Belgies"</string> <string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Bulgaars"</string> - <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) --> - <skip /> + <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Bulgaars, foneties"</string> <string name="keyboard_layout_italian" msgid="6497079660449781213">"Italiaans"</string> <string name="keyboard_layout_danish" msgid="8036432066627127851">"Deens"</string> <string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Noors"</string> diff --git a/packages/InputDevices/res/values-am/strings.xml b/packages/InputDevices/res/values-am/strings.xml index 7a4ffeff433a..1559fa890bcd 100644 --- a/packages/InputDevices/res/values-am/strings.xml +++ b/packages/InputDevices/res/values-am/strings.xml @@ -19,8 +19,7 @@ <string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"የስዊዝ ጀርመን"</string> <string name="keyboard_layout_belgian" msgid="2011984572838651558">"ቤልጂየም"</string> <string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"ቡልጋሪያ"</string> - <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) --> - <skip /> + <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"ቡልጋሪያኛ፣ ፎነቲክ"</string> <string name="keyboard_layout_italian" msgid="6497079660449781213">"ጣሊያንኛ"</string> <string name="keyboard_layout_danish" msgid="8036432066627127851">"ዴኒሽ"</string> <string name="keyboard_layout_norwegian" msgid="9090097917011040937">"ኖርዌጂያ"</string> diff --git a/packages/InputDevices/res/values-as/strings.xml b/packages/InputDevices/res/values-as/strings.xml index a63821e76633..49fbef92c114 100644 --- a/packages/InputDevices/res/values-as/strings.xml +++ b/packages/InputDevices/res/values-as/strings.xml @@ -19,8 +19,7 @@ <string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"ছুইছ জাৰ্মান"</string> <string name="keyboard_layout_belgian" msgid="2011984572838651558">"বেলজিয়ান"</string> <string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"বুলগেৰিয়ান"</string> - <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) --> - <skip /> + <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"বুলগেৰিয়ান ফ’নেটিক"</string> <string name="keyboard_layout_italian" msgid="6497079660449781213">"ইটালিয়ান"</string> <string name="keyboard_layout_danish" msgid="8036432066627127851">"ডেনিশ্ব"</string> <string name="keyboard_layout_norwegian" msgid="9090097917011040937">"ন\'ৰৱেয়ান"</string> diff --git a/packages/InputDevices/res/values-az/strings.xml b/packages/InputDevices/res/values-az/strings.xml index 16badc456e73..c5a1e1ee2e2e 100644 --- a/packages/InputDevices/res/values-az/strings.xml +++ b/packages/InputDevices/res/values-az/strings.xml @@ -19,8 +19,7 @@ <string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"İsveçrə Almanı"</string> <string name="keyboard_layout_belgian" msgid="2011984572838651558">"Belçikalı"</string> <string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Bolqar"</string> - <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) --> - <skip /> + <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Bolqar dili, Fonetika"</string> <string name="keyboard_layout_italian" msgid="6497079660449781213">"İtalyan"</string> <string name="keyboard_layout_danish" msgid="8036432066627127851">"Danimarkalı"</string> <string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Norveçli"</string> diff --git a/packages/InputDevices/res/values-b+sr+Latn/strings.xml b/packages/InputDevices/res/values-b+sr+Latn/strings.xml index 95ef45916031..16f1cb2fc591 100644 --- a/packages/InputDevices/res/values-b+sr+Latn/strings.xml +++ b/packages/InputDevices/res/values-b+sr+Latn/strings.xml @@ -19,8 +19,7 @@ <string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"švajcarsko nemačka"</string> <string name="keyboard_layout_belgian" msgid="2011984572838651558">"belgijska"</string> <string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"bugarska"</string> - <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) --> - <skip /> + <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"bugarska fonetska"</string> <string name="keyboard_layout_italian" msgid="6497079660449781213">"italijanska"</string> <string name="keyboard_layout_danish" msgid="8036432066627127851">"danska"</string> <string name="keyboard_layout_norwegian" msgid="9090097917011040937">"norveška"</string> diff --git a/packages/InputDevices/res/values-be/strings.xml b/packages/InputDevices/res/values-be/strings.xml index 63b899af1c98..6b0523f2bdf8 100644 --- a/packages/InputDevices/res/values-be/strings.xml +++ b/packages/InputDevices/res/values-be/strings.xml @@ -19,8 +19,7 @@ <string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Нямецкая (Швейцарыя)"</string> <string name="keyboard_layout_belgian" msgid="2011984572838651558">"Бельгійская"</string> <string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Балгарская"</string> - <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) --> - <skip /> + <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Балгарская фанетычная"</string> <string name="keyboard_layout_italian" msgid="6497079660449781213">"Італьянская"</string> <string name="keyboard_layout_danish" msgid="8036432066627127851">"Дацкая"</string> <string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Нарвежская"</string> diff --git a/packages/InputDevices/res/values-bg/strings.xml b/packages/InputDevices/res/values-bg/strings.xml index 90b7f6c5e9d3..a7088c930395 100644 --- a/packages/InputDevices/res/values-bg/strings.xml +++ b/packages/InputDevices/res/values-bg/strings.xml @@ -19,8 +19,7 @@ <string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"швейцарски немски"</string> <string name="keyboard_layout_belgian" msgid="2011984572838651558">"белгийски"</string> <string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"български"</string> - <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) --> - <skip /> + <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Българска фонетична клавиатура"</string> <string name="keyboard_layout_italian" msgid="6497079660449781213">"италиански"</string> <string name="keyboard_layout_danish" msgid="8036432066627127851">"датски"</string> <string name="keyboard_layout_norwegian" msgid="9090097917011040937">"норвежки"</string> diff --git a/packages/InputDevices/res/values-bs/strings.xml b/packages/InputDevices/res/values-bs/strings.xml index f6a229c34eb4..b92ac8cad498 100644 --- a/packages/InputDevices/res/values-bs/strings.xml +++ b/packages/InputDevices/res/values-bs/strings.xml @@ -19,8 +19,7 @@ <string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"švicarski njemački"</string> <string name="keyboard_layout_belgian" msgid="2011984572838651558">"belgijski"</string> <string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"bugarski"</string> - <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) --> - <skip /> + <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"bugarski, fonetski"</string> <string name="keyboard_layout_italian" msgid="6497079660449781213">"italijanski"</string> <string name="keyboard_layout_danish" msgid="8036432066627127851">"danski"</string> <string name="keyboard_layout_norwegian" msgid="9090097917011040937">"norveški"</string> diff --git a/packages/InputDevices/res/values-ca/strings.xml b/packages/InputDevices/res/values-ca/strings.xml index ec3b247ac6d3..a5b5e10129d4 100644 --- a/packages/InputDevices/res/values-ca/strings.xml +++ b/packages/InputDevices/res/values-ca/strings.xml @@ -19,8 +19,7 @@ <string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Alemany suís"</string> <string name="keyboard_layout_belgian" msgid="2011984572838651558">"Belga"</string> <string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Búlgar"</string> - <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) --> - <skip /> + <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Búlgar, fonètic"</string> <string name="keyboard_layout_italian" msgid="6497079660449781213">"Italià"</string> <string name="keyboard_layout_danish" msgid="8036432066627127851">"Danès"</string> <string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Noruec"</string> diff --git a/packages/InputDevices/res/values-da/strings.xml b/packages/InputDevices/res/values-da/strings.xml index dbe685cb2329..cf2aecf1783a 100644 --- a/packages/InputDevices/res/values-da/strings.xml +++ b/packages/InputDevices/res/values-da/strings.xml @@ -19,8 +19,7 @@ <string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Schweizertysk"</string> <string name="keyboard_layout_belgian" msgid="2011984572838651558">"Belgisk"</string> <string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Bulgarsk"</string> - <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) --> - <skip /> + <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Bulgarsk, fonetisk"</string> <string name="keyboard_layout_italian" msgid="6497079660449781213">"Italiensk"</string> <string name="keyboard_layout_danish" msgid="8036432066627127851">"Dansk"</string> <string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Norsk"</string> diff --git a/packages/InputDevices/res/values-de/strings.xml b/packages/InputDevices/res/values-de/strings.xml index fd7fca014548..1e786855ac25 100644 --- a/packages/InputDevices/res/values-de/strings.xml +++ b/packages/InputDevices/res/values-de/strings.xml @@ -19,8 +19,7 @@ <string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Schweizerdeutsch"</string> <string name="keyboard_layout_belgian" msgid="2011984572838651558">"Belgisch"</string> <string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Bulgarisch"</string> - <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) --> - <skip /> + <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Bulgarisch – phonetisch"</string> <string name="keyboard_layout_italian" msgid="6497079660449781213">"Italienisch"</string> <string name="keyboard_layout_danish" msgid="8036432066627127851">"Dänisch"</string> <string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Norwegisch"</string> diff --git a/packages/InputDevices/res/values-el/strings.xml b/packages/InputDevices/res/values-el/strings.xml index 8bdd6f8bc967..eb2cc9b09a09 100644 --- a/packages/InputDevices/res/values-el/strings.xml +++ b/packages/InputDevices/res/values-el/strings.xml @@ -19,8 +19,7 @@ <string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Γερμανικά Ελβετίας"</string> <string name="keyboard_layout_belgian" msgid="2011984572838651558">"Βελγικά"</string> <string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Βουλγαρικά"</string> - <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) --> - <skip /> + <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Βουλγαρικά (Φωνητικό)"</string> <string name="keyboard_layout_italian" msgid="6497079660449781213">"Ιταλικά"</string> <string name="keyboard_layout_danish" msgid="8036432066627127851">"Δανικά"</string> <string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Νορβηγικά"</string> diff --git a/packages/InputDevices/res/values-es-rUS/strings.xml b/packages/InputDevices/res/values-es-rUS/strings.xml index 5319f968afc3..e8d6597f0e57 100644 --- a/packages/InputDevices/res/values-es-rUS/strings.xml +++ b/packages/InputDevices/res/values-es-rUS/strings.xml @@ -19,8 +19,7 @@ <string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Alemán de Suiza"</string> <string name="keyboard_layout_belgian" msgid="2011984572838651558">"Belga"</string> <string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Búlgaro"</string> - <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) --> - <skip /> + <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Búlgaro fonético"</string> <string name="keyboard_layout_italian" msgid="6497079660449781213">"Italiano"</string> <string name="keyboard_layout_danish" msgid="8036432066627127851">"Danés"</string> <string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Noruego"</string> diff --git a/packages/InputDevices/res/values-es/strings.xml b/packages/InputDevices/res/values-es/strings.xml index 95b3b1c80cdf..9396e460eba1 100644 --- a/packages/InputDevices/res/values-es/strings.xml +++ b/packages/InputDevices/res/values-es/strings.xml @@ -19,8 +19,7 @@ <string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Alemán suizo"</string> <string name="keyboard_layout_belgian" msgid="2011984572838651558">"Belga"</string> <string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Búlgaro"</string> - <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) --> - <skip /> + <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Búlgaro (fonético)"</string> <string name="keyboard_layout_italian" msgid="6497079660449781213">"Italiano"</string> <string name="keyboard_layout_danish" msgid="8036432066627127851">"Danés"</string> <string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Noruego"</string> diff --git a/packages/InputDevices/res/values-et/strings.xml b/packages/InputDevices/res/values-et/strings.xml index a5fb8ac08502..cf28e9f66dae 100644 --- a/packages/InputDevices/res/values-et/strings.xml +++ b/packages/InputDevices/res/values-et/strings.xml @@ -19,8 +19,7 @@ <string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Šveitsisaksa"</string> <string name="keyboard_layout_belgian" msgid="2011984572838651558">"Belgia"</string> <string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Bulgaaria"</string> - <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) --> - <skip /> + <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Bulgaaria, foneetiline"</string> <string name="keyboard_layout_italian" msgid="6497079660449781213">"Itaalia"</string> <string name="keyboard_layout_danish" msgid="8036432066627127851">"Taani"</string> <string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Norra"</string> diff --git a/packages/InputDevices/res/values-eu/strings.xml b/packages/InputDevices/res/values-eu/strings.xml index 1c75a7c0f67f..1e080fc83141 100644 --- a/packages/InputDevices/res/values-eu/strings.xml +++ b/packages/InputDevices/res/values-eu/strings.xml @@ -19,8 +19,7 @@ <string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Alemana (Suitza)"</string> <string name="keyboard_layout_belgian" msgid="2011984572838651558">"Belgikarra"</string> <string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Bulgariarra"</string> - <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) --> - <skip /> + <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Bulgariarra, fonetikoa"</string> <string name="keyboard_layout_italian" msgid="6497079660449781213">"Italiarra"</string> <string name="keyboard_layout_danish" msgid="8036432066627127851">"Daniarra"</string> <string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Norvegiarra"</string> diff --git a/packages/InputDevices/res/values-fa/strings.xml b/packages/InputDevices/res/values-fa/strings.xml index ca7d43a1400f..5cb237e35053 100644 --- a/packages/InputDevices/res/values-fa/strings.xml +++ b/packages/InputDevices/res/values-fa/strings.xml @@ -19,8 +19,7 @@ <string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"آلمانی سوئیسی"</string> <string name="keyboard_layout_belgian" msgid="2011984572838651558">"بلژیکی"</string> <string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"بلغاری"</string> - <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) --> - <skip /> + <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"بلغاری، آوایی"</string> <string name="keyboard_layout_italian" msgid="6497079660449781213">"ایتالیایی"</string> <string name="keyboard_layout_danish" msgid="8036432066627127851">"دانمارکی"</string> <string name="keyboard_layout_norwegian" msgid="9090097917011040937">"نروژی"</string> diff --git a/packages/InputDevices/res/values-fi/strings.xml b/packages/InputDevices/res/values-fi/strings.xml index 2878c78318f7..da7210657381 100644 --- a/packages/InputDevices/res/values-fi/strings.xml +++ b/packages/InputDevices/res/values-fi/strings.xml @@ -19,8 +19,7 @@ <string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"sveitsinsaksa"</string> <string name="keyboard_layout_belgian" msgid="2011984572838651558">"belgialainen"</string> <string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"bulgaria"</string> - <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) --> - <skip /> + <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"bulgaria, foneettinen"</string> <string name="keyboard_layout_italian" msgid="6497079660449781213">"italia"</string> <string name="keyboard_layout_danish" msgid="8036432066627127851">"tanska"</string> <string name="keyboard_layout_norwegian" msgid="9090097917011040937">"norja"</string> diff --git a/packages/InputDevices/res/values-gl/strings.xml b/packages/InputDevices/res/values-gl/strings.xml index e1ca7cfefa1c..40ede04c8e05 100644 --- a/packages/InputDevices/res/values-gl/strings.xml +++ b/packages/InputDevices/res/values-gl/strings.xml @@ -19,8 +19,7 @@ <string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Alemán suízo"</string> <string name="keyboard_layout_belgian" msgid="2011984572838651558">"Belga"</string> <string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Búlgaro"</string> - <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) --> - <skip /> + <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Búlgaro, fonético"</string> <string name="keyboard_layout_italian" msgid="6497079660449781213">"Italiano"</string> <string name="keyboard_layout_danish" msgid="8036432066627127851">"Dinamarqués"</string> <string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Noruegués"</string> diff --git a/packages/InputDevices/res/values-gu/strings.xml b/packages/InputDevices/res/values-gu/strings.xml index bc2ee8303290..631fc1497ce4 100644 --- a/packages/InputDevices/res/values-gu/strings.xml +++ b/packages/InputDevices/res/values-gu/strings.xml @@ -19,8 +19,7 @@ <string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"સ્વિસ જર્મન"</string> <string name="keyboard_layout_belgian" msgid="2011984572838651558">"બેલ્જિયન"</string> <string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"બલ્ગેરિયન"</string> - <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) --> - <skip /> + <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"બલ્ગેરિયન ફોનેટિક"</string> <string name="keyboard_layout_italian" msgid="6497079660449781213">"ઇટાલિયન"</string> <string name="keyboard_layout_danish" msgid="8036432066627127851">"ડેનિશ"</string> <string name="keyboard_layout_norwegian" msgid="9090097917011040937">"નોર્વેજીયન"</string> diff --git a/packages/InputDevices/res/values-hr/strings.xml b/packages/InputDevices/res/values-hr/strings.xml index 0430f8662014..aff2a37c2b7f 100644 --- a/packages/InputDevices/res/values-hr/strings.xml +++ b/packages/InputDevices/res/values-hr/strings.xml @@ -19,8 +19,7 @@ <string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"švicarsko-njemačka"</string> <string name="keyboard_layout_belgian" msgid="2011984572838651558">"belgijska"</string> <string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"bugarska"</string> - <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) --> - <skip /> + <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"bugarska (fonetska)"</string> <string name="keyboard_layout_italian" msgid="6497079660449781213">"talijanska"</string> <string name="keyboard_layout_danish" msgid="8036432066627127851">"danska"</string> <string name="keyboard_layout_norwegian" msgid="9090097917011040937">"norveška"</string> diff --git a/packages/InputDevices/res/values-hu/strings.xml b/packages/InputDevices/res/values-hu/strings.xml index 76d10f544280..50d667b40b62 100644 --- a/packages/InputDevices/res/values-hu/strings.xml +++ b/packages/InputDevices/res/values-hu/strings.xml @@ -19,8 +19,7 @@ <string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"svájci német"</string> <string name="keyboard_layout_belgian" msgid="2011984572838651558">"belga"</string> <string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"bolgár"</string> - <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) --> - <skip /> + <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"bolgár fonetikus"</string> <string name="keyboard_layout_italian" msgid="6497079660449781213">"olasz"</string> <string name="keyboard_layout_danish" msgid="8036432066627127851">"dán"</string> <string name="keyboard_layout_norwegian" msgid="9090097917011040937">"norvég"</string> diff --git a/packages/InputDevices/res/values-hy/strings.xml b/packages/InputDevices/res/values-hy/strings.xml index fa4e24550639..4a6fe2b3be4f 100644 --- a/packages/InputDevices/res/values-hy/strings.xml +++ b/packages/InputDevices/res/values-hy/strings.xml @@ -19,8 +19,7 @@ <string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Շվեյցարական գերմաներեն"</string> <string name="keyboard_layout_belgian" msgid="2011984572838651558">"Բելգիական"</string> <string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Բուլղարերեն"</string> - <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) --> - <skip /> + <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"բուլղարերեն (հնչյունային)"</string> <string name="keyboard_layout_italian" msgid="6497079660449781213">"Իտալերեն"</string> <string name="keyboard_layout_danish" msgid="8036432066627127851">"Դանիերեն"</string> <string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Նորվեգերեն"</string> diff --git a/packages/InputDevices/res/values-in/strings.xml b/packages/InputDevices/res/values-in/strings.xml index f5d173a338a0..90ba97d16f6a 100644 --- a/packages/InputDevices/res/values-in/strings.xml +++ b/packages/InputDevices/res/values-in/strings.xml @@ -19,8 +19,7 @@ <string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Jerman Swiss"</string> <string name="keyboard_layout_belgian" msgid="2011984572838651558">"Belgia"</string> <string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Bulgaria"</string> - <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) --> - <skip /> + <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Bulgaria, Fonetik"</string> <string name="keyboard_layout_italian" msgid="6497079660449781213">"Italia"</string> <string name="keyboard_layout_danish" msgid="8036432066627127851">"Denmark"</string> <string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Norwegia"</string> diff --git a/packages/InputDevices/res/values-is/strings.xml b/packages/InputDevices/res/values-is/strings.xml index 09eedd300177..0889b21f3a64 100644 --- a/packages/InputDevices/res/values-is/strings.xml +++ b/packages/InputDevices/res/values-is/strings.xml @@ -19,8 +19,7 @@ <string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Svissneskt-þýskt"</string> <string name="keyboard_layout_belgian" msgid="2011984572838651558">"Belgískt"</string> <string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Búlgarskt"</string> - <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) --> - <skip /> + <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Búlgarskt hljóðritunarlyklaborð"</string> <string name="keyboard_layout_italian" msgid="6497079660449781213">"Ítalskt"</string> <string name="keyboard_layout_danish" msgid="8036432066627127851">"Danskt"</string> <string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Norskt"</string> diff --git a/packages/InputDevices/res/values-it/strings.xml b/packages/InputDevices/res/values-it/strings.xml index e15c01ff6a62..77f78c6f49d0 100644 --- a/packages/InputDevices/res/values-it/strings.xml +++ b/packages/InputDevices/res/values-it/strings.xml @@ -19,8 +19,7 @@ <string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Tedesco svizzero"</string> <string name="keyboard_layout_belgian" msgid="2011984572838651558">"Belga"</string> <string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Bulgaro"</string> - <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) --> - <skip /> + <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Bulgara, fonetica"</string> <string name="keyboard_layout_italian" msgid="6497079660449781213">"Italiano"</string> <string name="keyboard_layout_danish" msgid="8036432066627127851">"Danese"</string> <string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Norvegese"</string> diff --git a/packages/InputDevices/res/values-iw/strings.xml b/packages/InputDevices/res/values-iw/strings.xml index 4abdf87ec4d4..52641b2ba1ff 100644 --- a/packages/InputDevices/res/values-iw/strings.xml +++ b/packages/InputDevices/res/values-iw/strings.xml @@ -19,8 +19,7 @@ <string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"גרמנית שוויצרית"</string> <string name="keyboard_layout_belgian" msgid="2011984572838651558">"בלגית"</string> <string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"בולגרית"</string> - <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) --> - <skip /> + <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"בולגרית פונטית"</string> <string name="keyboard_layout_italian" msgid="6497079660449781213">"איטלקית"</string> <string name="keyboard_layout_danish" msgid="8036432066627127851">"דנית"</string> <string name="keyboard_layout_norwegian" msgid="9090097917011040937">"נורווגית"</string> diff --git a/packages/InputDevices/res/values-ja/strings.xml b/packages/InputDevices/res/values-ja/strings.xml index 606ab3cd2239..2961548599a6 100644 --- a/packages/InputDevices/res/values-ja/strings.xml +++ b/packages/InputDevices/res/values-ja/strings.xml @@ -19,8 +19,7 @@ <string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"ドイツ語(スイス)"</string> <string name="keyboard_layout_belgian" msgid="2011984572838651558">"ベルギー語"</string> <string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"ブルガリア語"</string> - <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) --> - <skip /> + <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"ブルガリア語(表音)"</string> <string name="keyboard_layout_italian" msgid="6497079660449781213">"イタリア語"</string> <string name="keyboard_layout_danish" msgid="8036432066627127851">"デンマーク語"</string> <string name="keyboard_layout_norwegian" msgid="9090097917011040937">"ノルウェー語"</string> diff --git a/packages/InputDevices/res/values-ka/strings.xml b/packages/InputDevices/res/values-ka/strings.xml index b4b1a2df4d30..2ccfeb2f635f 100644 --- a/packages/InputDevices/res/values-ka/strings.xml +++ b/packages/InputDevices/res/values-ka/strings.xml @@ -19,8 +19,7 @@ <string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"შვეიცარიული გერმანული"</string> <string name="keyboard_layout_belgian" msgid="2011984572838651558">"ბელგიური"</string> <string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"ბულგარული"</string> - <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) --> - <skip /> + <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"ბულგარული ფონეტიკური"</string> <string name="keyboard_layout_italian" msgid="6497079660449781213">"იტალიური"</string> <string name="keyboard_layout_danish" msgid="8036432066627127851">"დანიური"</string> <string name="keyboard_layout_norwegian" msgid="9090097917011040937">"ნორვეგიული"</string> diff --git a/packages/InputDevices/res/values-kn/strings.xml b/packages/InputDevices/res/values-kn/strings.xml index 8f62eb36a1f6..1e3c6932c3d4 100644 --- a/packages/InputDevices/res/values-kn/strings.xml +++ b/packages/InputDevices/res/values-kn/strings.xml @@ -19,8 +19,7 @@ <string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"ಸ್ವಿಸ್ ಜರ್ಮನ್"</string> <string name="keyboard_layout_belgian" msgid="2011984572838651558">"ಬೆಲ್ಜಿಯನ್"</string> <string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"ಬಲ್ಗೇರಿಯನ್"</string> - <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) --> - <skip /> + <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"ಬಲ್ಗೇರಿಯನ್ ಫೋನೆಟಿಕ್"</string> <string name="keyboard_layout_italian" msgid="6497079660449781213">"ಇಟಾಲಿಯನ್"</string> <string name="keyboard_layout_danish" msgid="8036432066627127851">"ಡ್ಯಾನಿಶ್"</string> <string name="keyboard_layout_norwegian" msgid="9090097917011040937">"ನಾರ್ವೇಜಿಯನ್"</string> diff --git a/packages/InputDevices/res/values-ko/strings.xml b/packages/InputDevices/res/values-ko/strings.xml index b1f658202c46..147050462eab 100644 --- a/packages/InputDevices/res/values-ko/strings.xml +++ b/packages/InputDevices/res/values-ko/strings.xml @@ -19,8 +19,7 @@ <string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"독일어(스위스)"</string> <string name="keyboard_layout_belgian" msgid="2011984572838651558">"벨기에어"</string> <string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"불가리아어"</string> - <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) --> - <skip /> + <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"불가리아어, 표음"</string> <string name="keyboard_layout_italian" msgid="6497079660449781213">"이탈리아어"</string> <string name="keyboard_layout_danish" msgid="8036432066627127851">"덴마크어"</string> <string name="keyboard_layout_norwegian" msgid="9090097917011040937">"노르웨이어"</string> diff --git a/packages/InputDevices/res/values-ky/strings.xml b/packages/InputDevices/res/values-ky/strings.xml index bc521a2cd6cb..cb9dbb2f0abb 100644 --- a/packages/InputDevices/res/values-ky/strings.xml +++ b/packages/InputDevices/res/values-ky/strings.xml @@ -19,8 +19,7 @@ <string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Немис (Швейцария)"</string> <string name="keyboard_layout_belgian" msgid="2011984572838651558">"Белгия"</string> <string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Болгар"</string> - <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) --> - <skip /> + <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Болгарча, фонетикалык"</string> <string name="keyboard_layout_italian" msgid="6497079660449781213">"Италия"</string> <string name="keyboard_layout_danish" msgid="8036432066627127851">"Дания"</string> <string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Норвег"</string> diff --git a/packages/InputDevices/res/values-lo/strings.xml b/packages/InputDevices/res/values-lo/strings.xml index edb59f3964ed..4ae4b7d9c58b 100644 --- a/packages/InputDevices/res/values-lo/strings.xml +++ b/packages/InputDevices/res/values-lo/strings.xml @@ -19,8 +19,7 @@ <string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"ສະວິສ ເຢຍລະມັນ"</string> <string name="keyboard_layout_belgian" msgid="2011984572838651558">"ເບວຢ້ຽນ"</string> <string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"ຮັງກາຣຽນ"</string> - <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) --> - <skip /> + <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"ບັງກາຣຽນ, ການອອກສຽງ"</string> <string name="keyboard_layout_italian" msgid="6497079660449781213">"ອິຕາລຽນ"</string> <string name="keyboard_layout_danish" msgid="8036432066627127851">"ເດັນນິຊ"</string> <string name="keyboard_layout_norwegian" msgid="9090097917011040937">"ນໍເວກຽນ"</string> diff --git a/packages/InputDevices/res/values-lt/strings.xml b/packages/InputDevices/res/values-lt/strings.xml index f33eb42e6a4f..d2aef7f057db 100644 --- a/packages/InputDevices/res/values-lt/strings.xml +++ b/packages/InputDevices/res/values-lt/strings.xml @@ -19,8 +19,7 @@ <string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Šveicarijos vokiečių k."</string> <string name="keyboard_layout_belgian" msgid="2011984572838651558">"Belgų k."</string> <string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Bulgarų k."</string> - <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) --> - <skip /> + <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Fonetinė bulgarų"</string> <string name="keyboard_layout_italian" msgid="6497079660449781213">"Italų k."</string> <string name="keyboard_layout_danish" msgid="8036432066627127851">"Danų k."</string> <string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Norvegų k."</string> diff --git a/packages/InputDevices/res/values-lv/strings.xml b/packages/InputDevices/res/values-lv/strings.xml index 4f47a3ba65db..8f3ff0ab2264 100644 --- a/packages/InputDevices/res/values-lv/strings.xml +++ b/packages/InputDevices/res/values-lv/strings.xml @@ -19,8 +19,7 @@ <string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Vācu (Šveice)"</string> <string name="keyboard_layout_belgian" msgid="2011984572838651558">"Beļģu"</string> <string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Bulgāru"</string> - <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) --> - <skip /> + <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Bulgāru, fonētiskā"</string> <string name="keyboard_layout_italian" msgid="6497079660449781213">"Itāļu"</string> <string name="keyboard_layout_danish" msgid="8036432066627127851">"Dāņu"</string> <string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Norvēģu"</string> diff --git a/packages/InputDevices/res/values-ml/strings.xml b/packages/InputDevices/res/values-ml/strings.xml index 65fbf22d5fe7..9e5344367eae 100644 --- a/packages/InputDevices/res/values-ml/strings.xml +++ b/packages/InputDevices/res/values-ml/strings.xml @@ -19,8 +19,7 @@ <string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"സ്വിസ് ജര്മന്"</string> <string name="keyboard_layout_belgian" msgid="2011984572838651558">"ബെൽജിയൻ"</string> <string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"ബൾഗേറിയൻ"</string> - <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) --> - <skip /> + <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"ബൾഗേറിയൻ, ഉച്ചാരണശബ്ദം"</string> <string name="keyboard_layout_italian" msgid="6497079660449781213">"ഇറ്റാലിയൻ"</string> <string name="keyboard_layout_danish" msgid="8036432066627127851">"ഡാനിഷ്"</string> <string name="keyboard_layout_norwegian" msgid="9090097917011040937">"നോർവീജിയൻ"</string> diff --git a/packages/InputDevices/res/values-mn/strings.xml b/packages/InputDevices/res/values-mn/strings.xml index a8fc6615f4c1..18c2faf7e464 100644 --- a/packages/InputDevices/res/values-mn/strings.xml +++ b/packages/InputDevices/res/values-mn/strings.xml @@ -19,8 +19,7 @@ <string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Швейцарийн Герман"</string> <string name="keyboard_layout_belgian" msgid="2011984572838651558">"Бельги"</string> <string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Болгар"</string> - <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) --> - <skip /> + <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Болгар хэл, Авиа зүй"</string> <string name="keyboard_layout_italian" msgid="6497079660449781213">"Итали"</string> <string name="keyboard_layout_danish" msgid="8036432066627127851">"Дани"</string> <string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Норвеги"</string> diff --git a/packages/InputDevices/res/values-mr/strings.xml b/packages/InputDevices/res/values-mr/strings.xml index da6caab3842e..d8788c97b90c 100644 --- a/packages/InputDevices/res/values-mr/strings.xml +++ b/packages/InputDevices/res/values-mr/strings.xml @@ -19,8 +19,7 @@ <string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"स्विस जर्मन"</string> <string name="keyboard_layout_belgian" msgid="2011984572838651558">"बेल्जियन"</string> <string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"बल्गेरियन"</string> - <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) --> - <skip /> + <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"बल्गेरियन, फोनेटिक"</string> <string name="keyboard_layout_italian" msgid="6497079660449781213">"इटालियन"</string> <string name="keyboard_layout_danish" msgid="8036432066627127851">"डॅनिश"</string> <string name="keyboard_layout_norwegian" msgid="9090097917011040937">"नॉर्वेजियन"</string> diff --git a/packages/InputDevices/res/values-ms/strings.xml b/packages/InputDevices/res/values-ms/strings.xml index 975024ba1cdf..8bc9b2a73cb2 100644 --- a/packages/InputDevices/res/values-ms/strings.xml +++ b/packages/InputDevices/res/values-ms/strings.xml @@ -19,8 +19,7 @@ <string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Jerman Switzerland"</string> <string name="keyboard_layout_belgian" msgid="2011984572838651558">"Bahasa Belgium"</string> <string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Bahasa Bulgaria"</string> - <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) --> - <skip /> + <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Bahasa Bulgaria, Fonetik"</string> <string name="keyboard_layout_italian" msgid="6497079660449781213">"Bahasa Itali"</string> <string name="keyboard_layout_danish" msgid="8036432066627127851">"Bahasa Denmark"</string> <string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Bahasa Norway"</string> diff --git a/packages/InputDevices/res/values-my/strings.xml b/packages/InputDevices/res/values-my/strings.xml index 5484d9d2a2e8..26720576ee91 100644 --- a/packages/InputDevices/res/values-my/strings.xml +++ b/packages/InputDevices/res/values-my/strings.xml @@ -19,8 +19,7 @@ <string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"ဆွစ် ဂျာမန်"</string> <string name="keyboard_layout_belgian" msgid="2011984572838651558">"ဘယ်လ်ဂျီယန်"</string> <string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"ဘူဂေးရီယန်း"</string> - <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) --> - <skip /> + <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"ဘူလ်ဂေးရီးယား အသံထွက်"</string> <string name="keyboard_layout_italian" msgid="6497079660449781213">"အီတာလီယန်"</string> <string name="keyboard_layout_danish" msgid="8036432066627127851">"ဒိန်းမတ်"</string> <string name="keyboard_layout_norwegian" msgid="9090097917011040937">"နောဝေဂျီယန်"</string> diff --git a/packages/InputDevices/res/values-nb/strings.xml b/packages/InputDevices/res/values-nb/strings.xml index 54840338b966..83b87e5dd45e 100644 --- a/packages/InputDevices/res/values-nb/strings.xml +++ b/packages/InputDevices/res/values-nb/strings.xml @@ -19,8 +19,7 @@ <string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Sveitsisk standardtysk"</string> <string name="keyboard_layout_belgian" msgid="2011984572838651558">"Belgisk"</string> <string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Bulgarsk"</string> - <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) --> - <skip /> + <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Bulgarsk, fonetisk"</string> <string name="keyboard_layout_italian" msgid="6497079660449781213">"Italiensk"</string> <string name="keyboard_layout_danish" msgid="8036432066627127851">"Dansk"</string> <string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Norsk"</string> diff --git a/packages/InputDevices/res/values-nl/strings.xml b/packages/InputDevices/res/values-nl/strings.xml index e000a30f9cf9..6e5849013fe7 100644 --- a/packages/InputDevices/res/values-nl/strings.xml +++ b/packages/InputDevices/res/values-nl/strings.xml @@ -19,8 +19,7 @@ <string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Zwitsers Duits"</string> <string name="keyboard_layout_belgian" msgid="2011984572838651558">"Belgisch"</string> <string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Bulgaars"</string> - <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) --> - <skip /> + <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Bulgaars, fonetisch"</string> <string name="keyboard_layout_italian" msgid="6497079660449781213">"Italiaans"</string> <string name="keyboard_layout_danish" msgid="8036432066627127851">"Deens"</string> <string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Noors"</string> diff --git a/packages/InputDevices/res/values-or/strings.xml b/packages/InputDevices/res/values-or/strings.xml index 6185ff77695d..aa16151752e6 100644 --- a/packages/InputDevices/res/values-or/strings.xml +++ b/packages/InputDevices/res/values-or/strings.xml @@ -19,8 +19,7 @@ <string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"ସୁଇସ୍ ଜର୍ମାନ୍"</string> <string name="keyboard_layout_belgian" msgid="2011984572838651558">"ବେଲ୍ଜିଆନ୍"</string> <string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"ବୁଲଗାରିଆନ୍"</string> - <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) --> - <skip /> + <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"ବୁଲଗେରିଆନ୍, ଫୋନେଟିକ୍"</string> <string name="keyboard_layout_italian" msgid="6497079660449781213">"ଇଟାଲିୟାନ୍"</string> <string name="keyboard_layout_danish" msgid="8036432066627127851">"ଡାନିଶ୍"</string> <string name="keyboard_layout_norwegian" msgid="9090097917011040937">"ନରୱେଜିଆନ୍"</string> diff --git a/packages/InputDevices/res/values-pa/strings.xml b/packages/InputDevices/res/values-pa/strings.xml index 97cf28bbc66c..7e5a03cde986 100644 --- a/packages/InputDevices/res/values-pa/strings.xml +++ b/packages/InputDevices/res/values-pa/strings.xml @@ -19,8 +19,7 @@ <string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"ਸਵਿਸ ਜਰਮਨ"</string> <string name="keyboard_layout_belgian" msgid="2011984572838651558">"ਬੈਲਜੀਅਨ"</string> <string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"ਬਲਗੇਰੀਅਨ"</string> - <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) --> - <skip /> + <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"ਬਲਗੇਰੀਅਨ, ਧੁਨੀਆਤਮਿਕ"</string> <string name="keyboard_layout_italian" msgid="6497079660449781213">"ਇਤਾਲਵੀ"</string> <string name="keyboard_layout_danish" msgid="8036432066627127851">"ਡੈਨਿਸ਼"</string> <string name="keyboard_layout_norwegian" msgid="9090097917011040937">"ਨਾਰਵੇਜੀਅਨ"</string> diff --git a/packages/InputDevices/res/values-pl/strings.xml b/packages/InputDevices/res/values-pl/strings.xml index 61819b624112..51755e2b2b52 100644 --- a/packages/InputDevices/res/values-pl/strings.xml +++ b/packages/InputDevices/res/values-pl/strings.xml @@ -19,8 +19,7 @@ <string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Niemiecki (Szwajcaria)"</string> <string name="keyboard_layout_belgian" msgid="2011984572838651558">"Belgijski"</string> <string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Bułgarski"</string> - <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) --> - <skip /> + <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Bułgarski (znaki fonetyczne)"</string> <string name="keyboard_layout_italian" msgid="6497079660449781213">"Włoski"</string> <string name="keyboard_layout_danish" msgid="8036432066627127851">"Duński"</string> <string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Norweski"</string> diff --git a/packages/InputDevices/res/values-pt-rBR/strings.xml b/packages/InputDevices/res/values-pt-rBR/strings.xml index 665a1c7632f1..128868856f5a 100644 --- a/packages/InputDevices/res/values-pt-rBR/strings.xml +++ b/packages/InputDevices/res/values-pt-rBR/strings.xml @@ -19,8 +19,7 @@ <string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Alemão suíço"</string> <string name="keyboard_layout_belgian" msgid="2011984572838651558">"Belga"</string> <string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Búlgaro"</string> - <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) --> - <skip /> + <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Búlgaro, fonético"</string> <string name="keyboard_layout_italian" msgid="6497079660449781213">"Italiano"</string> <string name="keyboard_layout_danish" msgid="8036432066627127851">"Dinamarquês"</string> <string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Norueguês"</string> diff --git a/packages/InputDevices/res/values-pt-rPT/strings.xml b/packages/InputDevices/res/values-pt-rPT/strings.xml index 1ccc64409452..89bb3e376678 100644 --- a/packages/InputDevices/res/values-pt-rPT/strings.xml +++ b/packages/InputDevices/res/values-pt-rPT/strings.xml @@ -19,8 +19,7 @@ <string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Alemão (Suíça)"</string> <string name="keyboard_layout_belgian" msgid="2011984572838651558">"Belga"</string> <string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Búlgaro"</string> - <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) --> - <skip /> + <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Búlgaro, fonético"</string> <string name="keyboard_layout_italian" msgid="6497079660449781213">"Italiano"</string> <string name="keyboard_layout_danish" msgid="8036432066627127851">"Dinamarquês"</string> <string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Norueguês"</string> diff --git a/packages/InputDevices/res/values-pt/strings.xml b/packages/InputDevices/res/values-pt/strings.xml index 665a1c7632f1..128868856f5a 100644 --- a/packages/InputDevices/res/values-pt/strings.xml +++ b/packages/InputDevices/res/values-pt/strings.xml @@ -19,8 +19,7 @@ <string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Alemão suíço"</string> <string name="keyboard_layout_belgian" msgid="2011984572838651558">"Belga"</string> <string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Búlgaro"</string> - <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) --> - <skip /> + <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Búlgaro, fonético"</string> <string name="keyboard_layout_italian" msgid="6497079660449781213">"Italiano"</string> <string name="keyboard_layout_danish" msgid="8036432066627127851">"Dinamarquês"</string> <string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Norueguês"</string> diff --git a/packages/InputDevices/res/values-ro/strings.xml b/packages/InputDevices/res/values-ro/strings.xml index e0b488585c78..f7ff2504f31f 100644 --- a/packages/InputDevices/res/values-ro/strings.xml +++ b/packages/InputDevices/res/values-ro/strings.xml @@ -19,8 +19,7 @@ <string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Germană (Elveția)"</string> <string name="keyboard_layout_belgian" msgid="2011984572838651558">"Belgiană"</string> <string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Bulgară"</string> - <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) --> - <skip /> + <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Bulgară fonetică"</string> <string name="keyboard_layout_italian" msgid="6497079660449781213">"Italiană"</string> <string name="keyboard_layout_danish" msgid="8036432066627127851">"Daneză"</string> <string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Norvegiană"</string> diff --git a/packages/InputDevices/res/values-ru/strings.xml b/packages/InputDevices/res/values-ru/strings.xml index 41ccf1a4f689..0cb4f34d4ab1 100644 --- a/packages/InputDevices/res/values-ru/strings.xml +++ b/packages/InputDevices/res/values-ru/strings.xml @@ -19,8 +19,7 @@ <string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"немецкий (Швейцария)"</string> <string name="keyboard_layout_belgian" msgid="2011984572838651558">"нидерландский (Бельгия)"</string> <string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"болгарский"</string> - <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) --> - <skip /> + <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"болгарский (фонетическая)"</string> <string name="keyboard_layout_italian" msgid="6497079660449781213">"итальянский"</string> <string name="keyboard_layout_danish" msgid="8036432066627127851">"датский"</string> <string name="keyboard_layout_norwegian" msgid="9090097917011040937">"норвежский"</string> diff --git a/packages/InputDevices/res/values-si/strings.xml b/packages/InputDevices/res/values-si/strings.xml index 4d355d73e985..eb3c44678db2 100644 --- a/packages/InputDevices/res/values-si/strings.xml +++ b/packages/InputDevices/res/values-si/strings.xml @@ -19,8 +19,7 @@ <string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"ස්විස් ජර්මන්"</string> <string name="keyboard_layout_belgian" msgid="2011984572838651558">"බෙල්ගියන්"</string> <string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"බල්ගේරියානු"</string> - <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) --> - <skip /> + <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"බල්ගේරියානු, ශබ්දිම"</string> <string name="keyboard_layout_italian" msgid="6497079660449781213">"ඉතාලි"</string> <string name="keyboard_layout_danish" msgid="8036432066627127851">"ඩෙන්මාර්ක"</string> <string name="keyboard_layout_norwegian" msgid="9090097917011040937">"නෝර්වීජියානු"</string> diff --git a/packages/InputDevices/res/values-sk/strings.xml b/packages/InputDevices/res/values-sk/strings.xml index c7ff2fd5d994..e7e15b0cbd00 100644 --- a/packages/InputDevices/res/values-sk/strings.xml +++ b/packages/InputDevices/res/values-sk/strings.xml @@ -19,8 +19,7 @@ <string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"švajčiarske (nemčina)"</string> <string name="keyboard_layout_belgian" msgid="2011984572838651558">"belgické"</string> <string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"bulharské"</string> - <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) --> - <skip /> + <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Bulharská fonetická klávesnica"</string> <string name="keyboard_layout_italian" msgid="6497079660449781213">"talianske"</string> <string name="keyboard_layout_danish" msgid="8036432066627127851">"dánske"</string> <string name="keyboard_layout_norwegian" msgid="9090097917011040937">"nórske"</string> diff --git a/packages/InputDevices/res/values-sl/strings.xml b/packages/InputDevices/res/values-sl/strings.xml index 68741b42e880..f4d1e5732fc5 100644 --- a/packages/InputDevices/res/values-sl/strings.xml +++ b/packages/InputDevices/res/values-sl/strings.xml @@ -19,8 +19,7 @@ <string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"švicarska nemška"</string> <string name="keyboard_layout_belgian" msgid="2011984572838651558">"belgijska"</string> <string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"bolgarska"</string> - <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) --> - <skip /> + <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"bolgarščina (fonetična)"</string> <string name="keyboard_layout_italian" msgid="6497079660449781213">"italijanska"</string> <string name="keyboard_layout_danish" msgid="8036432066627127851">"danska"</string> <string name="keyboard_layout_norwegian" msgid="9090097917011040937">"norveška"</string> diff --git a/packages/InputDevices/res/values-sr/strings.xml b/packages/InputDevices/res/values-sr/strings.xml index 2f68903c4e2d..e3a20438f34a 100644 --- a/packages/InputDevices/res/values-sr/strings.xml +++ b/packages/InputDevices/res/values-sr/strings.xml @@ -19,8 +19,7 @@ <string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"швајцарско немачка"</string> <string name="keyboard_layout_belgian" msgid="2011984572838651558">"белгијска"</string> <string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"бугарска"</string> - <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) --> - <skip /> + <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"бугарска фонетска"</string> <string name="keyboard_layout_italian" msgid="6497079660449781213">"италијанска"</string> <string name="keyboard_layout_danish" msgid="8036432066627127851">"данска"</string> <string name="keyboard_layout_norwegian" msgid="9090097917011040937">"норвешка"</string> diff --git a/packages/InputDevices/res/values-sv/strings.xml b/packages/InputDevices/res/values-sv/strings.xml index b465fa64ce82..097ada40be63 100644 --- a/packages/InputDevices/res/values-sv/strings.xml +++ b/packages/InputDevices/res/values-sv/strings.xml @@ -19,8 +19,7 @@ <string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Tyskt (Schweiz)"</string> <string name="keyboard_layout_belgian" msgid="2011984572838651558">"Belgiskt"</string> <string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Bulgariskt"</string> - <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) --> - <skip /> + <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Bulgariska (fonetiskt)"</string> <string name="keyboard_layout_italian" msgid="6497079660449781213">"Italienskt"</string> <string name="keyboard_layout_danish" msgid="8036432066627127851">"Danskt"</string> <string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Norskt"</string> diff --git a/packages/InputDevices/res/values-sw/strings.xml b/packages/InputDevices/res/values-sw/strings.xml index 794d907f006d..325796232d76 100644 --- a/packages/InputDevices/res/values-sw/strings.xml +++ b/packages/InputDevices/res/values-sw/strings.xml @@ -19,8 +19,7 @@ <string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Kijerumani cha Uswisi"</string> <string name="keyboard_layout_belgian" msgid="2011984572838651558">"Kibelgiji"</string> <string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Kibulgaria"</string> - <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) --> - <skip /> + <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Kibulgaria, Fonetiki"</string> <string name="keyboard_layout_italian" msgid="6497079660449781213">"Kiitaliano"</string> <string name="keyboard_layout_danish" msgid="8036432066627127851">"Kidenmarki"</string> <string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Kinorwei"</string> diff --git a/packages/InputDevices/res/values-te/strings.xml b/packages/InputDevices/res/values-te/strings.xml index d40c3e0d5d9f..c0253e528684 100644 --- a/packages/InputDevices/res/values-te/strings.xml +++ b/packages/InputDevices/res/values-te/strings.xml @@ -19,8 +19,7 @@ <string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"స్విస్ జర్మన్"</string> <string name="keyboard_layout_belgian" msgid="2011984572838651558">"బెల్జియన్"</string> <string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"బల్గేరియన్"</string> - <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) --> - <skip /> + <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"బల్గేరియన్, ఫోనెటిక్"</string> <string name="keyboard_layout_italian" msgid="6497079660449781213">"ఇటాలియన్"</string> <string name="keyboard_layout_danish" msgid="8036432066627127851">"డేనిష్"</string> <string name="keyboard_layout_norwegian" msgid="9090097917011040937">"నార్వేజియన్"</string> diff --git a/packages/InputDevices/res/values-th/strings.xml b/packages/InputDevices/res/values-th/strings.xml index 59136506abe0..c8e0e62f6e18 100644 --- a/packages/InputDevices/res/values-th/strings.xml +++ b/packages/InputDevices/res/values-th/strings.xml @@ -19,8 +19,7 @@ <string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"เยอรมันสวิส"</string> <string name="keyboard_layout_belgian" msgid="2011984572838651558">"เบลเยียม"</string> <string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"บัลแกเรีย"</string> - <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) --> - <skip /> + <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"ภาษาบัลแกเรีย ตามการออกเสียง"</string> <string name="keyboard_layout_italian" msgid="6497079660449781213">"อิตาลี"</string> <string name="keyboard_layout_danish" msgid="8036432066627127851">"เดนมาร์ก"</string> <string name="keyboard_layout_norwegian" msgid="9090097917011040937">"นอร์เวย์"</string> diff --git a/packages/InputDevices/res/values-tr/strings.xml b/packages/InputDevices/res/values-tr/strings.xml index a89cea59b30f..f093abb9a708 100644 --- a/packages/InputDevices/res/values-tr/strings.xml +++ b/packages/InputDevices/res/values-tr/strings.xml @@ -19,8 +19,7 @@ <string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"İsviçre Almancası"</string> <string name="keyboard_layout_belgian" msgid="2011984572838651558">"Belçika dili"</string> <string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Bulgarca"</string> - <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) --> - <skip /> + <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Bulgarca, Fonetik"</string> <string name="keyboard_layout_italian" msgid="6497079660449781213">"İtalyanca"</string> <string name="keyboard_layout_danish" msgid="8036432066627127851">"Danca"</string> <string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Norveççe"</string> diff --git a/packages/InputDevices/res/values-uk/strings.xml b/packages/InputDevices/res/values-uk/strings.xml index 4b37ca954d37..d9b58d21c14f 100644 --- a/packages/InputDevices/res/values-uk/strings.xml +++ b/packages/InputDevices/res/values-uk/strings.xml @@ -19,8 +19,7 @@ <string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"німецька (Швейцарія)"</string> <string name="keyboard_layout_belgian" msgid="2011984572838651558">"бельгійська"</string> <string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"болгарська"</string> - <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) --> - <skip /> + <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Болгарська (фонетична)"</string> <string name="keyboard_layout_italian" msgid="6497079660449781213">"італійська"</string> <string name="keyboard_layout_danish" msgid="8036432066627127851">"данська"</string> <string name="keyboard_layout_norwegian" msgid="9090097917011040937">"норвезька"</string> diff --git a/packages/InputDevices/res/values-ur/strings.xml b/packages/InputDevices/res/values-ur/strings.xml index ca42086b8811..2bff7ed2f234 100644 --- a/packages/InputDevices/res/values-ur/strings.xml +++ b/packages/InputDevices/res/values-ur/strings.xml @@ -19,8 +19,7 @@ <string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"سوئس جرمن"</string> <string name="keyboard_layout_belgian" msgid="2011984572838651558">"بیلجیئن"</string> <string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"بلغاریائی"</string> - <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) --> - <skip /> + <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"بلغاریائی، فونیٹک"</string> <string name="keyboard_layout_italian" msgid="6497079660449781213">"اطالوی"</string> <string name="keyboard_layout_danish" msgid="8036432066627127851">"ڈینش"</string> <string name="keyboard_layout_norwegian" msgid="9090097917011040937">"نارویجین"</string> diff --git a/packages/InputDevices/res/values-uz/strings.xml b/packages/InputDevices/res/values-uz/strings.xml index 77a06b5a4d79..9245aebd22ba 100644 --- a/packages/InputDevices/res/values-uz/strings.xml +++ b/packages/InputDevices/res/values-uz/strings.xml @@ -19,8 +19,7 @@ <string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Nemis (Shveytsariya)"</string> <string name="keyboard_layout_belgian" msgid="2011984572838651558">"Belgiyancha"</string> <string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Bolgarcha"</string> - <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) --> - <skip /> + <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Bolgar, fonetik"</string> <string name="keyboard_layout_italian" msgid="6497079660449781213">"Italyancha"</string> <string name="keyboard_layout_danish" msgid="8036432066627127851">"Datcha"</string> <string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Norvegcha"</string> diff --git a/packages/InputDevices/res/values-vi/strings.xml b/packages/InputDevices/res/values-vi/strings.xml index fd570efe8693..1b42ece3deed 100644 --- a/packages/InputDevices/res/values-vi/strings.xml +++ b/packages/InputDevices/res/values-vi/strings.xml @@ -19,8 +19,7 @@ <string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Tiếng Đức Thụy Sĩ"</string> <string name="keyboard_layout_belgian" msgid="2011984572838651558">"Tiếng Bỉ"</string> <string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Tiếng Bungary"</string> - <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) --> - <skip /> + <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Tiếng Bulgaria, Ngữ âm"</string> <string name="keyboard_layout_italian" msgid="6497079660449781213">"Tiếng Ý"</string> <string name="keyboard_layout_danish" msgid="8036432066627127851">"Tiếng Đan Mạch"</string> <string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Tiếng Na Uy"</string> diff --git a/packages/InputDevices/res/values-zh-rCN/strings.xml b/packages/InputDevices/res/values-zh-rCN/strings.xml index afc373acd61f..aa75605b5abd 100644 --- a/packages/InputDevices/res/values-zh-rCN/strings.xml +++ b/packages/InputDevices/res/values-zh-rCN/strings.xml @@ -19,8 +19,7 @@ <string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"瑞士德语"</string> <string name="keyboard_layout_belgian" msgid="2011984572838651558">"比利时语"</string> <string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"保加利亚语"</string> - <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) --> - <skip /> + <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"保加利亚语,注音"</string> <string name="keyboard_layout_italian" msgid="6497079660449781213">"意大利语"</string> <string name="keyboard_layout_danish" msgid="8036432066627127851">"丹麦语"</string> <string name="keyboard_layout_norwegian" msgid="9090097917011040937">"挪威语"</string> diff --git a/packages/InputDevices/res/values-zh-rHK/strings.xml b/packages/InputDevices/res/values-zh-rHK/strings.xml index 775fa2acfcec..dc824b97369d 100644 --- a/packages/InputDevices/res/values-zh-rHK/strings.xml +++ b/packages/InputDevices/res/values-zh-rHK/strings.xml @@ -19,8 +19,7 @@ <string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"德文(瑞士)"</string> <string name="keyboard_layout_belgian" msgid="2011984572838651558">"比利時文"</string> <string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"保加利亞文"</string> - <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) --> - <skip /> + <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"保加利亞文 (拼音)"</string> <string name="keyboard_layout_italian" msgid="6497079660449781213">"意大利文"</string> <string name="keyboard_layout_danish" msgid="8036432066627127851">"丹麥文"</string> <string name="keyboard_layout_norwegian" msgid="9090097917011040937">"挪威文"</string> diff --git a/packages/InputDevices/res/values-zh-rTW/strings.xml b/packages/InputDevices/res/values-zh-rTW/strings.xml index b4a059cbeb1f..c2714da0c3d9 100644 --- a/packages/InputDevices/res/values-zh-rTW/strings.xml +++ b/packages/InputDevices/res/values-zh-rTW/strings.xml @@ -19,8 +19,7 @@ <string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"德文 (瑞士)"</string> <string name="keyboard_layout_belgian" msgid="2011984572838651558">"比利時式"</string> <string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"保加利亞文"</string> - <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) --> - <skip /> + <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"保加利亞文 (拼音)"</string> <string name="keyboard_layout_italian" msgid="6497079660449781213">"義大利文"</string> <string name="keyboard_layout_danish" msgid="8036432066627127851">"丹麥文"</string> <string name="keyboard_layout_norwegian" msgid="9090097917011040937">"挪威文"</string> diff --git a/packages/InputDevices/res/values-zu/strings.xml b/packages/InputDevices/res/values-zu/strings.xml index 0a2499ad8967..3af1da1ac694 100644 --- a/packages/InputDevices/res/values-zu/strings.xml +++ b/packages/InputDevices/res/values-zu/strings.xml @@ -19,8 +19,7 @@ <string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Isi-Swiss German"</string> <string name="keyboard_layout_belgian" msgid="2011984572838651558">"Isi-Belgian"</string> <string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Isi-Bulgarian"</string> - <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) --> - <skip /> + <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Isi-Bulgarian, Ifonetiki"</string> <string name="keyboard_layout_italian" msgid="6497079660449781213">"Isi-Italian"</string> <string name="keyboard_layout_danish" msgid="8036432066627127851">"Isi-Danish"</string> <string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Isi-Norwegian"</string> diff --git a/packages/SettingsLib/res/values-hy/arrays.xml b/packages/SettingsLib/res/values-hy/arrays.xml index 2696f5a01785..a279872a088e 100644 --- a/packages/SettingsLib/res/values-hy/arrays.xml +++ b/packages/SettingsLib/res/values-hy/arrays.xml @@ -55,7 +55,7 @@ </string-array> <string-array name="hdcp_checking_summaries"> <item msgid="4045840870658484038">"Երբեք չօգտագործել HDCP ստուգումը"</item> - <item msgid="8254225038262324761">"Օգտագործել HDCP-ը` միայն DRM-ի բովանդակությունը ստուգելու համար"</item> + <item msgid="8254225038262324761">"Օգտագործել HDCP-ը՝ միայն DRM-ի բովանդակությունը ստուգելու համար"</item> <item msgid="6421717003037072581">"Միշտ օգտագործել HDCP ստուգումը"</item> </string-array> <string-array name="bt_hci_snoop_log_entries"> diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml index ffd95a45b61e..e434cac36b34 100644 --- a/packages/SettingsLib/res/values-hy/strings.xml +++ b/packages/SettingsLib/res/values-hy/strings.xml @@ -343,7 +343,7 @@ <string name="show_hw_layers_updates_summary" msgid="5850955890493054618">"Թարմացվելիս ընդգծել սարքաշարի ծածկույթները կանաչ գույնով"</string> <string name="debug_hw_overdraw" msgid="8944851091008756796">"Վրիպազերծել GPU գերազանցումները"</string> <string name="disable_overlays" msgid="4206590799671557143">"Կասեցնել HW վրադրումները"</string> - <string name="disable_overlays_summary" msgid="1954852414363338166">"Միշտ օգտագործել GPU-ն` էկրանի կազմման համար"</string> + <string name="disable_overlays_summary" msgid="1954852414363338166">"Միշտ օգտագործել GPU-ն՝ էկրանի կազմման համար"</string> <string name="simulate_color_space" msgid="1206503300335835151">"Նմանակել գունատարածքը"</string> <string name="enable_opengl_traces_title" msgid="4638773318659125196">"Ակտիվացնել OpenGL հետքերը"</string> <string name="usb_audio_disable_routing" msgid="3367656923544254975">"Անջատել USB աուդիո երթուղումը"</string> diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java index bcd2ff71b57f..4db61b02e8e9 100644 --- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java +++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java @@ -175,6 +175,8 @@ public class SecureSettings { Settings.Secure.SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED, Settings.Secure.PANIC_GESTURE_ENABLED, Settings.Secure.PANIC_SOUND_ENABLED, - Settings.Secure.ADAPTIVE_CONNECTIVITY_ENABLED + Settings.Secure.ADAPTIVE_CONNECTIVITY_ENABLED, + Settings.Secure.ASSIST_HANDLES_LEARNING_TIME_ELAPSED_MILLIS, + Settings.Secure.ASSIST_HANDLES_LEARNING_EVENT_COUNT }; } diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java index 3630f257f583..1fde40c37d9c 100644 --- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java +++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java @@ -264,5 +264,8 @@ public class SecureSettingsValidators { VALIDATORS.put(Secure.PANIC_GESTURE_ENABLED, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.PANIC_SOUND_ENABLED, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.ADAPTIVE_CONNECTIVITY_ENABLED, BOOLEAN_VALIDATOR); + VALIDATORS.put( + Secure.ASSIST_HANDLES_LEARNING_TIME_ELAPSED_MILLIS, NONE_NEGATIVE_LONG_VALIDATOR); + VALIDATORS.put(Secure.ASSIST_HANDLES_LEARNING_EVENT_COUNT, NON_NEGATIVE_INTEGER_VALIDATOR); } } diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java index e7ec8b4e1f65..bc6660184fe3 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java @@ -1875,6 +1875,15 @@ class SettingsProtoDumpUtil { SecureSettingsProto.Assist.GESTURE_SETUP_COMPLETE); p.end(assistToken); + final long assistHandlesToken = p.start(SecureSettingsProto.ASSIST_HANDLES); + dumpSetting(s, p, + Settings.Secure.ASSIST_HANDLES_LEARNING_TIME_ELAPSED_MILLIS, + SecureSettingsProto.AssistHandles.LEARNING_TIME_ELAPSED_MILLIS); + dumpSetting(s, p, + Settings.Secure.ASSIST_HANDLES_LEARNING_EVENT_COUNT, + SecureSettingsProto.AssistHandles.LEARNING_EVENT_COUNT); + p.end(assistHandlesToken); + final long autofillToken = p.start(SecureSettingsProto.AUTOFILL); dumpSetting(s, p, Settings.Secure.AUTOFILL_SERVICE, diff --git a/packages/SoundPicker/src/com/android/soundpicker/RingtonePickerActivity.java b/packages/SoundPicker/src/com/android/soundpicker/RingtonePickerActivity.java index d69f3d620d48..504e18a1488e 100644 --- a/packages/SoundPicker/src/com/android/soundpicker/RingtonePickerActivity.java +++ b/packages/SoundPicker/src/com/android/soundpicker/RingtonePickerActivity.java @@ -587,15 +587,19 @@ public final class RingtonePickerActivity extends AlertActivity implements } private Uri getCurrentlySelectedRingtoneUri() { - if (getCheckedItem() == mDefaultRingtonePos) { - // Use the default Uri that they originally gave us. - return mUriForDefaultItem; - } else if (getCheckedItem() == mSilentPos) { - // Use a null Uri for the 'Silent' item. - return null; - } else { - return mRingtoneManager.getRingtoneUri(getRingtoneManagerPosition(getCheckedItem())); - } + if (getCheckedItem() == POS_UNKNOWN) { + // When the getCheckItem is POS_UNKNOWN, it is not the case we expected. + // We return null for this case. + return null; + } else if (getCheckedItem() == mDefaultRingtonePos) { + // Use the default Uri that they originally gave us. + return mUriForDefaultItem; + } else if (getCheckedItem() == mSilentPos) { + // Use a null Uri for the 'Silent' item. + return null; + } else { + return mRingtoneManager.getRingtoneUri(getRingtoneManagerPosition(getCheckedItem())); + } } private void saveAnyPlayingRingtone() { diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index a1b9dcd9c028..cf78a131d6fb 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -776,6 +776,7 @@ android:exported="true"> <intent-filter> <action android:name="com.android.systemui.action.LAUNCH_MEDIA_OUTPUT_DIALOG" /> + <action android:name="com.android.systemui.action.DISMISS_MEDIA_OUTPUT_DIALOG" /> </intent-filter> </receiver> diff --git a/packages/SystemUI/res-keyguard/values-km/strings.xml b/packages/SystemUI/res-keyguard/values-km/strings.xml index 24b5c23a6732..52b7fab768c5 100644 --- a/packages/SystemUI/res-keyguard/values-km/strings.xml +++ b/packages/SystemUI/res-keyguard/values-km/strings.xml @@ -100,7 +100,7 @@ <string name="kg_pin_accepted" msgid="1625501841604389716">"កូដត្រូវបានទទួលយក!"</string> <string name="keyguard_carrier_default" msgid="6359808469637388586">"គ្មានសេវាទេ។"</string> <string name="accessibility_ime_switch_button" msgid="9082358310194861329">"ប្ដូរវិធីបញ្ចូល"</string> - <string name="airplane_mode" msgid="2528005343938497866">"មុខងារពេលជិះយន្តហោះ"</string> + <string name="airplane_mode" msgid="2528005343938497866">"ពេលជិះយន្តហោះ"</string> <string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"តម្រូវឲ្យប្រើលំនាំ បន្ទាប់ពីឧបករណ៍ចាប់ផ្តើមឡើងវិញ"</string> <string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"តម្រូវឲ្យបញ្ចូលកូដ PIN បន្ទាប់ពីឧបករណ៍ចាប់ផ្តើមឡើងវិញ"</string> <string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"តម្រូវឲ្យបញ្ចូលពាក្យសម្ងាត់ បន្ទាប់ពីឧបករណ៍ចាប់ផ្តើមឡើងវិញ"</string> diff --git a/packages/SystemUI/res/layout/media_output_dialog.xml b/packages/SystemUI/res/layout/media_output_dialog.xml index 0229e6e9d4dd..73beefc9da83 100644 --- a/packages/SystemUI/res/layout/media_output_dialog.xml +++ b/packages/SystemUI/res/layout/media_output_dialog.xml @@ -32,7 +32,7 @@ android:id="@+id/header_icon" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:paddingEnd="16dp"/> + android:paddingEnd="@dimen/media_output_dialog_header_icon_padding"/> <LinearLayout android:layout_width="match_parent" @@ -70,36 +70,14 @@ android:id="@+id/device_list" android:layout_width="match_parent" android:layout_height="wrap_content" - android:gravity="start|center_vertical" android:orientation="vertical"> - <View - android:layout_width="match_parent" - android:layout_height="12dp"/> - - <include - layout="@layout/media_output_list_item" - android:id="@+id/group_item_controller" - android:visibility="gone"/> - - <View - android:id="@+id/group_item_divider" - android:layout_width="match_parent" - android:layout_height="1dp" - android:background="?android:attr/listDivider" - android:visibility="gone"/> - <androidx.recyclerview.widget.RecyclerView android:id="@+id/list_result" android:scrollbars="vertical" android:layout_width="match_parent" android:layout_height="wrap_content" android:overScrollMode="never"/> - - <View - android:id="@+id/list_bottom_padding" - android:layout_width="match_parent" - android:layout_height="12dp"/> </LinearLayout> <View diff --git a/packages/SystemUI/res/layout/notification_conversation_info.xml b/packages/SystemUI/res/layout/notification_conversation_info.xml index fd89c0bf39dd..10ad8291636e 100644 --- a/packages/SystemUI/res/layout/notification_conversation_info.xml +++ b/packages/SystemUI/res/layout/notification_conversation_info.xml @@ -34,7 +34,7 @@ android:layout_height="wrap_content" android:gravity="center_vertical" android:clipChildren="false" - android:paddingTop="11dp" + android:paddingTop="@dimen/notification_guts_header_top_padding" android:clipToPadding="true"> <ImageView android:id="@+id/conversation_icon" diff --git a/packages/SystemUI/res/layout/notification_info.xml b/packages/SystemUI/res/layout/notification_info.xml index 92b3ff335cba..0a33d5e4429b 100644 --- a/packages/SystemUI/res/layout/notification_info.xml +++ b/packages/SystemUI/res/layout/notification_info.xml @@ -30,10 +30,11 @@ <LinearLayout android:id="@+id/header" android:layout_width="match_parent" - android:layout_height="@dimen/notification_guts_conversation_header_height" + android:layout_height="wrap_content" android:gravity="center_vertical" android:clipChildren="false" - android:clipToPadding="false"> + android:paddingTop="@dimen/notification_guts_header_top_padding" + android:clipToPadding="true"> <ImageView android:id="@+id/pkg_icon" android:layout_width="@dimen/notification_guts_conversation_icon_size" diff --git a/packages/SystemUI/res/layout/partial_conversation_info.xml b/packages/SystemUI/res/layout/partial_conversation_info.xml index c353d089895c..af66f8ba4cd6 100644 --- a/packages/SystemUI/res/layout/partial_conversation_info.xml +++ b/packages/SystemUI/res/layout/partial_conversation_info.xml @@ -30,10 +30,11 @@ <LinearLayout android:id="@+id/header" android:layout_width="match_parent" - android:layout_height="@dimen/notification_guts_conversation_header_height" + android:layout_height="wrap_content" android:gravity="center_vertical" android:clipChildren="false" - android:clipToPadding="false"> + android:paddingTop="@dimen/notification_guts_header_top_padding" + android:clipToPadding="true"> <ImageView android:id="@+id/icon" android:layout_width="@dimen/notification_guts_conversation_icon_size" diff --git a/packages/SystemUI/res/layout/window_magnifier_view.xml b/packages/SystemUI/res/layout/window_magnifier_view.xml index 6ced97836358..efd24c7d9d4f 100644 --- a/packages/SystemUI/res/layout/window_magnifier_view.xml +++ b/packages/SystemUI/res/layout/window_magnifier_view.xml @@ -17,24 +17,26 @@ <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="wrap_content" - android:layout_height="wrap_content"> - + android:layout_height="wrap_content" + android:screenReaderFocusable="true"> <View android:layout_width="match_parent" android:layout_height="match_parent" android:layout_margin="@dimen/magnification_outer_border_margin" + android:importantForAccessibility="no" android:background="@android:color/black"/> <View android:layout_width="match_parent" android:layout_height="match_parent" android:layout_margin="@dimen/magnification_inner_border_margin" + android:importantForAccessibility="no" android:background="@color/magnification_border_color"/> <RelativeLayout android:layout_width="match_parent" android:layout_height="match_parent" - android:orientation="vertical"> + android:importantForAccessibility="noHideDescendants"> <View android:id="@+id/left_handle" @@ -76,6 +78,7 @@ android:layout_margin="@dimen/magnification_outer_border_margin" android:layout_gravity="right|bottom" android:scaleType="center" + android:importantForAccessibility="no" android:src="@drawable/ic_move_magnification"/> </FrameLayout>
\ 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 556c39091cce..72dfe74b2fde 100644 --- a/packages/SystemUI/res/values-af/strings.xml +++ b/packages/SystemUI/res/values-af/strings.xml @@ -879,20 +879,13 @@ </string-array> <string name="tuner_low_priority" msgid="8412666814123009820">"Wys laeprioriteit-kennisgewingikone"</string> <string name="other" msgid="429768510980739978">"Ander"</string> - <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) --> - <skip /> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"verwyder teël"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"voeg teël aan einde by"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Skuif teël"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Voeg teël by"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Skuif na <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Voeg by posisie <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Posisie <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Kitsinstellingswysiger."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g>-kennisgewing: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Maak instellings oop."</string> diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml index c8462976528c..0ec236de8867 100644 --- a/packages/SystemUI/res/values-am/strings.xml +++ b/packages/SystemUI/res/values-am/strings.xml @@ -879,20 +879,13 @@ </string-array> <string name="tuner_low_priority" msgid="8412666814123009820">"አነስተኛ ቅድሚያ ያላቸው የማሳወቂያ አዶዎችን አሳይ"</string> <string name="other" msgid="429768510980739978">"ሌላ"</string> - <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) --> - <skip /> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"ሰቅ አስወግድ"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"ሰቅ መጨረሻው ላይ አክል"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"ሰቁን ውሰድ"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"ሰቅ ያክሉ"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"ወደ <xliff:g id="POSITION">%1$d</xliff:g> ውሰድ"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"ወደ <xliff:g id="POSITION">%1$d</xliff:g> ቦታ አክል"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"የ<xliff:g id="POSITION">%1$d</xliff:g> አቀማመጥ"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"የፈጣን ቅንብሮች አርታዒ።"</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"የ<xliff:g id="ID_1">%1$s</xliff:g> ማሳወቂያ፦ <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"ቅንብሮችን ክፈት።"</string> diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml index 785f8feb551a..e1d195028dcb 100644 --- a/packages/SystemUI/res/values-ar/strings.xml +++ b/packages/SystemUI/res/values-ar/strings.xml @@ -899,20 +899,13 @@ </string-array> <string name="tuner_low_priority" msgid="8412666814123009820">"إظهار رموز الإشعارات ذات الأولوية المنخفضة"</string> <string name="other" msgid="429768510980739978">"غير ذلك"</string> - <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) --> - <skip /> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"إزالة البطاقة"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"إضافة بطاقة إلى النهاية"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"نقل البطاقة"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"إضافة بطاقة"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"الانتقال إلى <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"الإضافة إلى الموضع <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"الموضع: <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"برنامج تعديل الإعدادات السريعة."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"إشعار <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"فتح الإعدادات."</string> diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml index 059c4bca8181..61ba6dc1265c 100644 --- a/packages/SystemUI/res/values-as/strings.xml +++ b/packages/SystemUI/res/values-as/strings.xml @@ -879,20 +879,13 @@ </string-array> <string name="tuner_low_priority" msgid="8412666814123009820">"কম গুৰুত্বপূৰ্ণ জাননীৰ আইকনসমূহ দেখুৱাওক"</string> <string name="other" msgid="429768510980739978">"অন্যান্য"</string> - <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) --> - <skip /> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"টাইল আঁতৰাবলৈ"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"টাইল শেষত যোগ দিবলৈ"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"টাইল স্থানান্তৰ কৰক"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"টাইল যোগ দিয়ক"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g> নম্বৰলৈ স্থানান্তৰ কৰক"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g> নম্বৰ স্থানত যোগ দিয়ক"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"<xliff:g id="POSITION">%1$d</xliff:g> নম্বৰ স্থান"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"ক্ষিপ্ৰ ছেটিংসমূহৰ সম্পাদক।"</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> জাননী: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"ছেটিংসমূহ খোলক।"</string> diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml index 2381d3330730..bee0e5fdca54 100644 --- a/packages/SystemUI/res/values-az/strings.xml +++ b/packages/SystemUI/res/values-az/strings.xml @@ -879,20 +879,13 @@ </string-array> <string name="tuner_low_priority" msgid="8412666814123009820">"Aşağı prioritet bildiriş işarələrini göstərin"</string> <string name="other" msgid="429768510980739978">"Digər"</string> - <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) --> - <skip /> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"lövhəni silin"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"sona lövhə əlavə edin"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Lövhəni köçürün"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Lövhə əlavə edin"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g> mövqeyinə köçürün"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g> mövqeyinə əlavə edin"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"<xliff:g id="POSITION">%1$d</xliff:g> mövqeyi"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Sürətli ayarlar redaktoru."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> bildiriş: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Ayarları açın."</string> diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml index 55abfda98bc8..5966356d1e66 100644 --- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml +++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml @@ -884,20 +884,13 @@ </string-array> <string name="tuner_low_priority" msgid="8412666814123009820">"Prikaži ikone obaveštenja niskog prioriteta"</string> <string name="other" msgid="429768510980739978">"Drugo"</string> - <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) --> - <skip /> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"uklonili pločicu"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"dodali pločicu na kraj"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Premestite pločicu"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Dodajte pločicu"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Premestite na <xliff:g id="POSITION">%1$d</xliff:g>. poziciju"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Dodajte na <xliff:g id="POSITION">%1$d</xliff:g>. poziciju"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"<xliff:g id="POSITION">%1$d</xliff:g>. pozicija"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Uređivač za Brza podešavanja."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Obaveštenja za <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Otvori Podešavanja."</string> diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml index 47f9861f640b..cedc3753da42 100644 --- a/packages/SystemUI/res/values-be/strings.xml +++ b/packages/SystemUI/res/values-be/strings.xml @@ -889,20 +889,13 @@ </string-array> <string name="tuner_low_priority" msgid="8412666814123009820">"Паказваць значкі апавяшчэнняў з нізкім прыярытэтам"</string> <string name="other" msgid="429768510980739978">"Іншае"</string> - <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) --> - <skip /> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"выдаліць плітку"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"дадаць плітку ў канец"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Перамясціць плітку"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Дадаць плітку"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Перамясціць на пазіцыю <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Дадаць на пазіцыю <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Пазіцыя <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Рэдактар хуткіх налад."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Апавяшчэнне <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Адкрыць налады."</string> diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml index efe12673fe78..14088404e81b 100644 --- a/packages/SystemUI/res/values-bg/strings.xml +++ b/packages/SystemUI/res/values-bg/strings.xml @@ -879,20 +879,13 @@ </string-array> <string name="tuner_low_priority" msgid="8412666814123009820">"Показване на иконите за известията с нисък приоритет"</string> <string name="other" msgid="429768510980739978">"Друго"</string> - <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) --> - <skip /> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"премахване на панел"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"добавяне на панел в края"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Преместване на панел"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Добавяне на панел"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Преместване към позиция <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Добавяне към позиция <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Позиция <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Редактор за бързи настройки."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Известие от <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Отваряне на настройките."</string> diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml index 811a971ab02b..41a2671466df 100644 --- a/packages/SystemUI/res/values-bs/strings.xml +++ b/packages/SystemUI/res/values-bs/strings.xml @@ -884,20 +884,13 @@ </string-array> <string name="tuner_low_priority" msgid="8412666814123009820">"Prikaži ikone obavještenja niskog prioriteta"</string> <string name="other" msgid="429768510980739978">"Ostalo"</string> - <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) --> - <skip /> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"uklanjanje kartice"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"dodavanje kartice na kraj"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Pomjeranje kartice"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Dodavanje kartice"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Pomjeranje u položaj <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Dodavanje u položaj <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Položaj <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Uređivanje brzih postavki"</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> obavještenje: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Otvori postavke."</string> diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml index 793c44b4544b..783f7877e230 100644 --- a/packages/SystemUI/res/values-ca/strings.xml +++ b/packages/SystemUI/res/values-ca/strings.xml @@ -92,10 +92,10 @@ <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Processant gravació de pantalla"</string> <string name="screenrecord_channel_description" msgid="4147077128486138351">"Notificació en curs d\'una sessió de gravació de la pantalla"</string> <string name="screenrecord_start_label" msgid="1750350278888217473">"Vols iniciar la gravació?"</string> - <string name="screenrecord_description" msgid="1123231719680353736">"Quan graves contingut, el sistema Android pot capturar qualsevol informació sensible que es mostri a la pantalla o que es reprodueixi al dispositiu. Això inclou les contrasenyes, la informació de pagament, les fotos, els missatges i l\'àudio."</string> + <string name="screenrecord_description" msgid="1123231719680353736">"Durant la gravació, el sistema Android pot capturar qualsevol informació sensible que es mostri a la pantalla o que es reprodueixi al dispositiu. Això inclou contrasenyes, informació de pagament, fotos, missatges i àudio."</string> <string name="screenrecord_audio_label" msgid="6183558856175159629">"Grava l\'àudio"</string> <string name="screenrecord_device_audio_label" msgid="9016927171280567791">"Àudio del dispositiu"</string> - <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"Sons del dispositiu, com ara la música, les trucades i els sons de trucada"</string> + <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"So del dispositiu, com ara música, trucades i sons de trucada"</string> <string name="screenrecord_mic_label" msgid="2111264835791332350">"Micròfon"</string> <string name="screenrecord_device_audio_and_mic_label" msgid="1831323771978646841">"Àudio del dispositiu i micròfon"</string> <string name="screenrecord_start" msgid="330991441575775004">"Inicia"</string> @@ -387,7 +387,7 @@ <string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"La Wi‑Fi no està connectada"</string> <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Brillantor"</string> <string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="2325362583903258677">"AUTOMÀTICA"</string> - <string name="quick_settings_inversion_label" msgid="5078769633069667698">"Inverteix els colors"</string> + <string name="quick_settings_inversion_label" msgid="5078769633069667698">"Inverteix colors"</string> <string name="quick_settings_color_space_label" msgid="537528291083575559">"Mode de correcció de color"</string> <string name="quick_settings_more_settings" msgid="2878235926753776694">"Més opcions"</string> <string name="quick_settings_done" msgid="2163641301648855793">"Fet"</string> @@ -879,20 +879,13 @@ </string-array> <string name="tuner_low_priority" msgid="8412666814123009820">"Mostra les icones de notificació amb prioritat baixa"</string> <string name="other" msgid="429768510980739978">"Altres"</string> - <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) --> - <skip /> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"suprimir el mosaic"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"afegir una targeta al final"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Mou la targeta"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Afegeix una targeta"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Mou a la posició <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Afegeix a la posició <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Posició <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor de configuració ràpida."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Notificació de <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Obre la configuració."</string> diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml index 1d9238932283..f2e8a3f908f3 100644 --- a/packages/SystemUI/res/values-da/strings.xml +++ b/packages/SystemUI/res/values-da/strings.xml @@ -879,20 +879,13 @@ </string-array> <string name="tuner_low_priority" msgid="8412666814123009820">"Vis ikoner for notifikationer med lav prioritet"</string> <string name="other" msgid="429768510980739978">"Andet"</string> - <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) --> - <skip /> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"fjern kortet"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"føj kortet til slutningen"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Flyt kortet"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Tilføj et kort"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Flyt til <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Føj til placering <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Placering <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Redigeringsværktøj til Kvikmenu."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g>-notifikation: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Åbn Indstillinger."</string> diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml index 5976a1c95362..887aad5d9aa6 100644 --- a/packages/SystemUI/res/values-de/strings.xml +++ b/packages/SystemUI/res/values-de/strings.xml @@ -879,20 +879,13 @@ </string-array> <string name="tuner_low_priority" msgid="8412666814123009820">"Symbole für Benachrichtigungen mit einer niedrigen Priorität anzeigen"</string> <string name="other" msgid="429768510980739978">"Sonstiges"</string> - <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) --> - <skip /> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"die Kachel zu entfernen"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"die Kachel am Ende hinzuzufügen"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Kachel verschieben"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Kachel hinzufügen"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Auf Position <xliff:g id="POSITION">%1$d</xliff:g> verschieben"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Zur Position <xliff:g id="POSITION">%1$d</xliff:g> hinzufügen"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Position <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor für Schnelleinstellungen."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Benachrichtigung von <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Einstellungen öffnen."</string> diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml index 1eae5d4a12f9..1b912c7a6ed9 100644 --- a/packages/SystemUI/res/values-el/strings.xml +++ b/packages/SystemUI/res/values-el/strings.xml @@ -879,20 +879,13 @@ </string-array> <string name="tuner_low_priority" msgid="8412666814123009820">"Εμφάνιση εικονιδίων ειδοποιήσεων χαμηλής προτεραιότητας"</string> <string name="other" msgid="429768510980739978">"Άλλο"</string> - <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) --> - <skip /> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"κατάργηση πλακιδίου"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"προσθήκη πλακιδίου στο τέλος"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Μετακίνηση πλακιδίου"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Προσθήκη πλακιδίου"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Μετακίνηση στη θέση <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Προσθήκη στη θέση <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Θέση <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Επεξεργασία γρήγορων ρυθμίσεων."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Ειδοποίηση <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Άνοιγμα ρυθμίσεων."</string> diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml index 4ea0341f8c39..065c17bec41a 100644 --- a/packages/SystemUI/res/values-en-rAU/strings.xml +++ b/packages/SystemUI/res/values-en-rAU/strings.xml @@ -92,7 +92,7 @@ <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Processing screen recording"</string> <string name="screenrecord_channel_description" msgid="4147077128486138351">"Ongoing notification for a screen record session"</string> <string name="screenrecord_start_label" msgid="1750350278888217473">"Start recording?"</string> - <string name="screenrecord_description" msgid="1123231719680353736">"While recording, Android System can capture any sensitive information that’s visible on your screen or played on your device. This includes passwords, payment info, photos, messages and audio."</string> + <string name="screenrecord_description" msgid="1123231719680353736">"While recording, the Android System can capture any sensitive information that’s visible on your screen or played on your device. This includes passwords, payment info, photos, messages and audio."</string> <string name="screenrecord_audio_label" msgid="6183558856175159629">"Record audio"</string> <string name="screenrecord_device_audio_label" msgid="9016927171280567791">"Device audio"</string> <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"Sound from your device, like music, calls and ringtones"</string> diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml index 3916e58ba076..1cc662521645 100644 --- a/packages/SystemUI/res/values-en-rCA/strings.xml +++ b/packages/SystemUI/res/values-en-rCA/strings.xml @@ -92,7 +92,7 @@ <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Processing screen recording"</string> <string name="screenrecord_channel_description" msgid="4147077128486138351">"Ongoing notification for a screen record session"</string> <string name="screenrecord_start_label" msgid="1750350278888217473">"Start recording?"</string> - <string name="screenrecord_description" msgid="1123231719680353736">"While recording, Android System can capture any sensitive information that’s visible on your screen or played on your device. This includes passwords, payment info, photos, messages and audio."</string> + <string name="screenrecord_description" msgid="1123231719680353736">"While recording, the Android System can capture any sensitive information that’s visible on your screen or played on your device. This includes passwords, payment info, photos, messages and audio."</string> <string name="screenrecord_audio_label" msgid="6183558856175159629">"Record audio"</string> <string name="screenrecord_device_audio_label" msgid="9016927171280567791">"Device audio"</string> <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"Sound from your device, like music, calls and ringtones"</string> diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml index 4ea0341f8c39..065c17bec41a 100644 --- a/packages/SystemUI/res/values-en-rGB/strings.xml +++ b/packages/SystemUI/res/values-en-rGB/strings.xml @@ -92,7 +92,7 @@ <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Processing screen recording"</string> <string name="screenrecord_channel_description" msgid="4147077128486138351">"Ongoing notification for a screen record session"</string> <string name="screenrecord_start_label" msgid="1750350278888217473">"Start recording?"</string> - <string name="screenrecord_description" msgid="1123231719680353736">"While recording, Android System can capture any sensitive information that’s visible on your screen or played on your device. This includes passwords, payment info, photos, messages and audio."</string> + <string name="screenrecord_description" msgid="1123231719680353736">"While recording, the Android System can capture any sensitive information that’s visible on your screen or played on your device. This includes passwords, payment info, photos, messages and audio."</string> <string name="screenrecord_audio_label" msgid="6183558856175159629">"Record audio"</string> <string name="screenrecord_device_audio_label" msgid="9016927171280567791">"Device audio"</string> <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"Sound from your device, like music, calls and ringtones"</string> diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml index 4ea0341f8c39..065c17bec41a 100644 --- a/packages/SystemUI/res/values-en-rIN/strings.xml +++ b/packages/SystemUI/res/values-en-rIN/strings.xml @@ -92,7 +92,7 @@ <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Processing screen recording"</string> <string name="screenrecord_channel_description" msgid="4147077128486138351">"Ongoing notification for a screen record session"</string> <string name="screenrecord_start_label" msgid="1750350278888217473">"Start recording?"</string> - <string name="screenrecord_description" msgid="1123231719680353736">"While recording, Android System can capture any sensitive information that’s visible on your screen or played on your device. This includes passwords, payment info, photos, messages and audio."</string> + <string name="screenrecord_description" msgid="1123231719680353736">"While recording, the Android System can capture any sensitive information that’s visible on your screen or played on your device. This includes passwords, payment info, photos, messages and audio."</string> <string name="screenrecord_audio_label" msgid="6183558856175159629">"Record audio"</string> <string name="screenrecord_device_audio_label" msgid="9016927171280567791">"Device audio"</string> <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"Sound from your device, like music, calls and ringtones"</string> diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml index b1ee41f4c53c..dfd42c3ea54f 100644 --- a/packages/SystemUI/res/values-es-rUS/strings.xml +++ b/packages/SystemUI/res/values-es-rUS/strings.xml @@ -879,20 +879,13 @@ </string-array> <string name="tuner_low_priority" msgid="8412666814123009820">"Mostrar íconos de notificaciones con prioridad baja"</string> <string name="other" msgid="429768510980739978">"Otros"</string> - <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) --> - <skip /> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"Quitar tarjeta"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"Agregar tarjeta al final"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Mover la tarjeta"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Agregar tarjeta"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Mover a <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Agregar a la posición <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Posición <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor de Configuración rápida"</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Notificación de <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Abrir Configuración"</string> diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml index 7e5361b4f70c..20b50abd5b08 100644 --- a/packages/SystemUI/res/values-es/strings.xml +++ b/packages/SystemUI/res/values-es/strings.xml @@ -879,20 +879,13 @@ </string-array> <string name="tuner_low_priority" msgid="8412666814123009820">"Mostrar iconos de notificaciones con prioridad baja"</string> <string name="other" msgid="429768510980739978">"Otros"</string> - <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) --> - <skip /> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"quitar icono"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"añadir icono al final"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Mover icono"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Añadir icono"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Mover a <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Añadir a la posición <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Posición <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor de ajustes rápidos."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Notificación de <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Abrir ajustes."</string> diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml index 78de793517f4..ced06044f331 100644 --- a/packages/SystemUI/res/values-et/strings.xml +++ b/packages/SystemUI/res/values-et/strings.xml @@ -879,20 +879,13 @@ </string-array> <string name="tuner_low_priority" msgid="8412666814123009820">"Kuva madala prioriteediga märguande ikoonid"</string> <string name="other" msgid="429768510980739978">"Muu"</string> - <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) --> - <skip /> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"paani eemaldamiseks"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"paani lõppu lisamiseks"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Teisalda paan"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Lisa paan"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Teisaldamine asendisse <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Lisamine asendisse <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Asend <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Kiirseadete redigeerija."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Teenuse <xliff:g id="ID_1">%1$s</xliff:g> märguanne: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Ava seaded."</string> diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml index b5d84447271a..c1ab20c9ebb6 100644 --- a/packages/SystemUI/res/values-eu/strings.xml +++ b/packages/SystemUI/res/values-eu/strings.xml @@ -879,20 +879,13 @@ </string-array> <string name="tuner_low_priority" msgid="8412666814123009820">"Erakutsi lehentasun txikiko jakinarazpenen ikonoak"</string> <string name="other" msgid="429768510980739978">"Beste bat"</string> - <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) --> - <skip /> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"kendu lauza"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"gehitu lauza amaieran"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Aldatu tokiz lauza"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Gehitu lauza"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Eraman <xliff:g id="POSITION">%1$d</xliff:g>garren kokapenera"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Gehitu <xliff:g id="POSITION">%1$d</xliff:g>garren kokapenean"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Kokapena: <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Ezarpen bizkorren editorea."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> zerbitzuaren jakinarazpena: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Ireki ezarpenak."</string> diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml index d453f91da504..f6cf179d7d00 100644 --- a/packages/SystemUI/res/values-fa/strings.xml +++ b/packages/SystemUI/res/values-fa/strings.xml @@ -879,20 +879,13 @@ </string-array> <string name="tuner_low_priority" msgid="8412666814123009820">"نمایش نمادهای اعلان کماهمیت"</string> <string name="other" msgid="429768510980739978">"موارد دیگر"</string> - <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) --> - <skip /> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"برداشتن کاشی"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"افزودن کاشی به انتها"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"انتقال کاشی"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"افزودن کاشی"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"انتقال به <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"افزودن به موقعیت <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"موقعیت <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"ویرایشگر تنظیمات سریع."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"اعلان <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"باز کردن تنظیمات."</string> @@ -967,7 +960,7 @@ <string name="slice_permission_text_2" msgid="6758906940360746983">"- میتواند در <xliff:g id="APP">%1$s</xliff:g> اقدام انجام دهد"</string> <string name="slice_permission_checkbox" msgid="4242888137592298523">"به <xliff:g id="APP">%1$s</xliff:g> اجازه داده شود تکههایی از برنامهها نشان دهد"</string> <string name="slice_permission_allow" msgid="6340449521277951123">"مجاز"</string> - <string name="slice_permission_deny" msgid="6870256451658176895">"رد کردن"</string> + <string name="slice_permission_deny" msgid="6870256451658176895">"مجاز نبودن"</string> <string name="auto_saver_title" msgid="6873691178754086596">"برای زمانبندی «بهینهسازی باتری» ضربه بزنید"</string> <string name="auto_saver_text" msgid="3214960308353838764">"وقتی باتری روبهاتمام است، بهینهسازی باتری را روشن کنید"</string> <string name="no_auto_saver_action" msgid="7467924389609773835">"نه متشکرم"</string> diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml index dde2449d8894..3d9a6dbee1f9 100644 --- a/packages/SystemUI/res/values-fi/strings.xml +++ b/packages/SystemUI/res/values-fi/strings.xml @@ -379,7 +379,7 @@ <string name="quick_settings_wifi_on_label" msgid="2489928193654318511">"Wi-Fi on käytössä"</string> <string name="quick_settings_wifi_detail_empty_text" msgid="483130889414601732">"Ei Wi-Fi-verkkoja käytettävissä"</string> <string name="quick_settings_wifi_secondary_label_transient" msgid="7501659015509357887">"Otetaan käyttöön…"</string> - <string name="quick_settings_cast_title" msgid="2279220930629235211">"Näytön suoratoisto"</string> + <string name="quick_settings_cast_title" msgid="2279220930629235211">"Näytön striimaus"</string> <string name="quick_settings_casting" msgid="1435880708719268055">"Lähetetään"</string> <string name="quick_settings_cast_device_default_name" msgid="6988469571141331700">"Nimetön laite"</string> <string name="quick_settings_cast_device_default_description" msgid="2580520859212250265">"Valmis lähetystä varten"</string> @@ -879,20 +879,13 @@ </string-array> <string name="tuner_low_priority" msgid="8412666814123009820">"Näytä vähemmän tärkeät ilmoituskuvakkeet"</string> <string name="other" msgid="429768510980739978">"Muu"</string> - <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) --> - <skip /> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"poista kiekko"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"lisää kiekko loppuun"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Siirrä kiekkoa"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Lisää kiekko"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Siirrä paikkaan <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Lisää paikkaan <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Paikka <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Pika-asetusten muokkausnäkymä"</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Ilmoitus kohteesta <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Avaa asetukset."</string> diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml index aba2d8fd389a..9075a69b2ed8 100644 --- a/packages/SystemUI/res/values-gl/strings.xml +++ b/packages/SystemUI/res/values-gl/strings.xml @@ -879,20 +879,13 @@ </string-array> <string name="tuner_low_priority" msgid="8412666814123009820">"Mostrar iconas das notificacións que teñan baixa prioridade"</string> <string name="other" msgid="429768510980739978">"Outros"</string> - <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) --> - <skip /> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"quitar tarxeta"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"engadir tarxeta ao final"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Mover tarxeta"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Engadir tarxeta"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Mover a <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Engadir á posición <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Posición <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor de configuración rápida."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Notificación de <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Abrir configuración."</string> diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml index 1acef350034a..e8b446063922 100644 --- a/packages/SystemUI/res/values-gu/strings.xml +++ b/packages/SystemUI/res/values-gu/strings.xml @@ -879,20 +879,13 @@ </string-array> <string name="tuner_low_priority" msgid="8412666814123009820">"ઓછી પ્રાધાન્યતાનું નોટિફિકેશન આઇકન બતાવો"</string> <string name="other" msgid="429768510980739978">"અન્ય"</string> - <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) --> - <skip /> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"ટાઇલ કાઢી નાખો"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"ટાઇલને અંતે ઉમેરો"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"ટાઇલ ખસેડો"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"ટાઇલ ઉમેરો"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g> પર ખસેડો"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"જગ્યા પર <xliff:g id="POSITION">%1$d</xliff:g> ઉમેરો"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"જગ્યા <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"ઝડપી સેટિંગ્સ સંપાદક."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> નોટિફિકેશન: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"સેટિંગ્સ ખોલો."</string> diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml index 9b3f99748375..649299b1cd7d 100644 --- a/packages/SystemUI/res/values-hr/strings.xml +++ b/packages/SystemUI/res/values-hr/strings.xml @@ -884,20 +884,13 @@ </string-array> <string name="tuner_low_priority" msgid="8412666814123009820">"Prikaži ikone obavijesti niskog prioriteta"</string> <string name="other" msgid="429768510980739978">"Ostalo"</string> - <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) --> - <skip /> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"uklanjanje kartice"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"dodavanje kartice na kraj"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Premještanje kartice"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Dodavanje kartice"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Premještanje u prostoriju <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Dodavanje na položaj <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Položaj <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Uređivač brzih postavki."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> obavijest: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Otvaranje postavki."</string> diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml index 7f4fa9d0acd3..f658bb76c649 100644 --- a/packages/SystemUI/res/values-hu/strings.xml +++ b/packages/SystemUI/res/values-hu/strings.xml @@ -879,20 +879,13 @@ </string-array> <string name="tuner_low_priority" msgid="8412666814123009820">"Alacsony prioritású értesítési ikonok mutatása"</string> <string name="other" msgid="429768510980739978">"Egyéb"</string> - <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) --> - <skip /> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"mozaik eltávolításához"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"mozaiknak a végéhez való hozzáadásához"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Mozaik áthelyezése"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Mozaik hozzáadása"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Áthelyezés ide: <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Hozzáadás a következő pozícióhoz: <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"<xliff:g id="POSITION">%1$d</xliff:g>. hely"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Gyorsbeállítások szerkesztője"</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g>-értesítések: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Beállítások megnyitása."</string> diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml index 70cbf493feca..28c79fbc8852 100644 --- a/packages/SystemUI/res/values-hy/strings.xml +++ b/packages/SystemUI/res/values-hy/strings.xml @@ -879,20 +879,13 @@ </string-array> <string name="tuner_low_priority" msgid="8412666814123009820">"Ցուցադրել ցածր առաջնահերթության ծանուցումների պատկերակները"</string> <string name="other" msgid="429768510980739978">"Այլ"</string> - <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) --> - <skip /> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"հեռացնել սալիկը"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"ավելացնել սալիկ վերջում"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Տեղափոխել սալիկը"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Ավելացնել սալիկ"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Տեղափոխել դիրք <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Ավելացնել դիրք <xliff:g id="POSITION">%1$d</xliff:g>-ում"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Դիրք <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Արագ կարգավորումների խմբագրիչ:"</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> ծանուցում՝ <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Բացել կարգավորումները:"</string> diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml index dc0a5128b808..12f42e84c3db 100644 --- a/packages/SystemUI/res/values-in/strings.xml +++ b/packages/SystemUI/res/values-in/strings.xml @@ -862,7 +862,7 @@ <string name="left_icon" msgid="5036278531966897006">"Ikon kiri"</string> <string name="right_icon" msgid="1103955040645237425">"Ikon kanan"</string> <string name="drag_to_add_tiles" msgid="8933270127508303672">"Tahan dan tarik untuk menambahkan kartu"</string> - <string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Tahan dan tarik untuk mengatur ulang kartu"</string> + <string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Tahan dan tarik untuk menata ulang kartu"</string> <string name="drag_to_remove_tiles" msgid="4682194717573850385">"Tarik ke sini untuk menghapus"</string> <string name="drag_to_remove_disabled" msgid="933046987838658850">"Anda membutuhkan setidaknya <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> kartu"</string> <string name="qs_edit" msgid="5583565172803472437">"Edit"</string> @@ -879,20 +879,13 @@ </string-array> <string name="tuner_low_priority" msgid="8412666814123009820">"Tampilkan ikon notifikasi prioritas rendah"</string> <string name="other" msgid="429768510980739978">"Lainnya"</string> - <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) --> - <skip /> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"menghapus kartu"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"menambahkan kartu ke akhir"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Pindahkan kartu"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Tambahkan kartu"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Pindahkan ke <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Tambahkan ke posisi <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Posisi <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor setelan cepat."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Notifikasi <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Buka setelan."</string> diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml index f79796b6276d..1f8f2aa2fc27 100644 --- a/packages/SystemUI/res/values-is/strings.xml +++ b/packages/SystemUI/res/values-is/strings.xml @@ -861,10 +861,10 @@ <string name="right_keycode" msgid="2480715509844798438">"Lykiltákn til hægri"</string> <string name="left_icon" msgid="5036278531966897006">"Tákn til vinstri"</string> <string name="right_icon" msgid="1103955040645237425">"Tákn til hægri"</string> - <string name="drag_to_add_tiles" msgid="8933270127508303672">"Haltu inni og dragðu til að bæta við reitum"</string> + <string name="drag_to_add_tiles" msgid="8933270127508303672">"Haltu inni og dragðu til að bæta við flísum"</string> <string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Haltu og dragðu til að endurraða flísum"</string> <string name="drag_to_remove_tiles" msgid="4682194717573850385">"Dragðu hingað til að fjarlægja"</string> - <string name="drag_to_remove_disabled" msgid="933046987838658850">"Reitirnir mega ekki vera færri en <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g>"</string> + <string name="drag_to_remove_disabled" msgid="933046987838658850">"Flísarnar mega ekki vera færri en <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g>"</string> <string name="qs_edit" msgid="5583565172803472437">"Breyta"</string> <string name="tuner_time" msgid="2450785840990529997">"Tími"</string> <string-array name="clock_options"> @@ -879,20 +879,13 @@ </string-array> <string name="tuner_low_priority" msgid="8412666814123009820">"Sýna tákn fyrir tilkynningar með litlum forgangi"</string> <string name="other" msgid="429768510980739978">"Annað"</string> - <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) --> - <skip /> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"fjarlægja reit"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"bæta reit við aftast"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Færa reit"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Bæta reit við"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Færa í <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Bæta við í stöðu <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Staða <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Flýtistillingaritill."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> tilkynning: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Opna stillingar."</string> diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml index 3b99dc2e1f6e..eb76a4b58fcd 100644 --- a/packages/SystemUI/res/values-it/strings.xml +++ b/packages/SystemUI/res/values-it/strings.xml @@ -879,20 +879,13 @@ </string-array> <string name="tuner_low_priority" msgid="8412666814123009820">"Mostra icone di notifiche con priorità bassa"</string> <string name="other" msgid="429768510980739978">"Altro"</string> - <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) --> - <skip /> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"rimuovere il riquadro"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"aggiungere il riquadro alla fine"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Sposta riquadro"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Aggiungi riquadro"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Sposta nella posizione <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Aggiungi alla posizione <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Posizione <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor di impostazioni rapide."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Notifica di <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Apri le impostazioni."</string> @@ -975,7 +968,7 @@ <string name="auto_saver_enabled_text" msgid="7889491183116752719">"Il Risparmio energetico verrà attivato automaticamente quando la carica della batteria sarà inferiore a <xliff:g id="PERCENTAGE">%d</xliff:g>%%."</string> <string name="open_saver_setting_action" msgid="2111461909782935190">"Impostazioni"</string> <string name="auto_saver_okay_action" msgid="7815925750741935386">"OK"</string> - <string name="heap_dump_tile_name" msgid="2464189856478823046">"Esegui dump heap SysUI"</string> + <string name="heap_dump_tile_name" msgid="2464189856478823046">"Dump heap SysUI"</string> <string name="ongoing_privacy_chip_content_single_app" msgid="2969750601815230385">"L\'app <xliff:g id="APP">%1$s</xliff:g> sta usando <xliff:g id="TYPES_LIST">%2$s</xliff:g>."</string> <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Le app stanno usando <xliff:g id="TYPES_LIST">%s</xliff:g>."</string> <string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string> diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml index 3bb8ea5fe1dd..31e6befca5ff 100644 --- a/packages/SystemUI/res/values-iw/strings.xml +++ b/packages/SystemUI/res/values-iw/strings.xml @@ -889,20 +889,13 @@ </string-array> <string name="tuner_low_priority" msgid="8412666814123009820">"הצגת סמלי התראות בעדיפות נמוכה"</string> <string name="other" msgid="429768510980739978">"אחר"</string> - <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) --> - <skip /> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"הסרת האריח"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"הוספת האריח לקצה"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"הזזת האריח"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"הוספת אריח"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"העברה אל <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"הוספה למיקום <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"מיקום <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"עורך הגדרות מהירות."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"התראות <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"פתיחת הגדרות."</string> diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml index 2c55569bb96a..b358f3d866dc 100644 --- a/packages/SystemUI/res/values-ja/strings.xml +++ b/packages/SystemUI/res/values-ja/strings.xml @@ -879,20 +879,13 @@ </string-array> <string name="tuner_low_priority" msgid="8412666814123009820">"優先度の低い通知アイコンを表示"</string> <string name="other" msgid="429768510980739978">"その他"</string> - <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) --> - <skip /> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"タイルを削除"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"タイルを最後に追加"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"タイルを移動"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"タイルを追加"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g> に移動"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"ポジション <xliff:g id="POSITION">%1$d</xliff:g> に追加"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"位置: <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"クイック設定エディタ"</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> の通知: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"設定を開きます。"</string> diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml index ca5b7da19247..4e85d608a72b 100644 --- a/packages/SystemUI/res/values-ka/strings.xml +++ b/packages/SystemUI/res/values-ka/strings.xml @@ -879,20 +879,13 @@ </string-array> <string name="tuner_low_priority" msgid="8412666814123009820">"დაბალი პრიორიტეტის მქონე შეტყობინებების ხატულების ჩვენება"</string> <string name="other" msgid="429768510980739978">"სხვა"</string> - <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) --> - <skip /> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"მოზაიკის ფილის წაშლა"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"ფილის ბოლოში დამატება"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"მოზაიკის გადატანა"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"მოზაიკის დამატება"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"გადატანა <xliff:g id="POSITION">%1$d</xliff:g>-ზე"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"დამატება პოზიციაზე <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"პოზიცია <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"სწრაფი პარამეტრების რედაქტორი."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> შეტყობინება: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"პარამეტრების გახსნა."</string> diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml index a4bf498edfff..f17210602b83 100644 --- a/packages/SystemUI/res/values-kk/strings.xml +++ b/packages/SystemUI/res/values-kk/strings.xml @@ -865,7 +865,7 @@ <string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Элементтердің ретін өзгерту үшін оларды басып тұрып сүйреңіз"</string> <string name="drag_to_remove_tiles" msgid="4682194717573850385">"Керексіздерін осы жерге сүйреңіз"</string> <string name="drag_to_remove_disabled" msgid="933046987838658850">"Кемінде <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> бөлшек қажет."</string> - <string name="qs_edit" msgid="5583565172803472437">"Өңдеу"</string> + <string name="qs_edit" msgid="5583565172803472437">"Өзгерту"</string> <string name="tuner_time" msgid="2450785840990529997">"Уақыт"</string> <string-array name="clock_options"> <item msgid="3986445361435142273">"Сағаттарды, минуттарды және секундтарды көрсету"</item> @@ -879,20 +879,13 @@ </string-array> <string name="tuner_low_priority" msgid="8412666814123009820">"Маңызды емес хабарландыру белгішелерін көрсету"</string> <string name="other" msgid="429768510980739978">"Басқа"</string> - <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) --> - <skip /> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"бөлшекті өшіру"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"бөлшекті соңына қосу"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Бөлшекті жылжыту"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Бөлшек қосу"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g> орнына жылжыту"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g> орнына қосу"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"<xliff:g id="POSITION">%1$d</xliff:g> орны"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Жылдам параметрлер өңдегіші."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> хабарландыруы: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Параметрлерді ашу."</string> diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml index 7eaa3039947a..b126a11d1310 100644 --- a/packages/SystemUI/res/values-km/strings.xml +++ b/packages/SystemUI/res/values-km/strings.xml @@ -879,20 +879,13 @@ </string-array> <string name="tuner_low_priority" msgid="8412666814123009820">"បង្ហាញរូបការជូនដំណឹងដែលមានអាទិភាពទាប"</string> <string name="other" msgid="429768510980739978">"ផ្សេងៗ"</string> - <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) --> - <skip /> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"ដកប្រអប់ចេញ"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"បញ្ចូលប្រអប់ទៅខាងចុង"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"ផ្លាស់ទីប្រអប់"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"បញ្ចូលប្រអប់"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"ផ្លាស់ទីទៅ <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"បញ្ចូលទៅទីតាំងទី <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"ទីតាំងទី <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"កម្មវិធីកែការកំណត់រហ័ស"</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> ការជូនដំណឹង៖ <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"បើកការកំណត់"</string> diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml index 79f845290365..475677be3845 100644 --- a/packages/SystemUI/res/values-kn/strings.xml +++ b/packages/SystemUI/res/values-kn/strings.xml @@ -387,7 +387,7 @@ <string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"ವೈ-ಫೈ ಸಂಪರ್ಕಗೊಂಡಿಲ್ಲ"</string> <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"ಪ್ರಕಾಶಮಾನ"</string> <string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="2325362583903258677">"ಸ್ವಯಂ"</string> - <string name="quick_settings_inversion_label" msgid="5078769633069667698">"ಬಣ್ಣಗಳನ್ನು ಬದಲಾಯಿಸಿ"</string> + <string name="quick_settings_inversion_label" msgid="5078769633069667698">"ಬಣ್ಣಗಳನ್ನು ಇನ್ವರ್ಟ್ ಮಾಡಿ"</string> <string name="quick_settings_color_space_label" msgid="537528291083575559">"ಬಣ್ಣ ತಿದ್ದುಪಡಿ ಮೋಡ್"</string> <string name="quick_settings_more_settings" msgid="2878235926753776694">"ಹೆಚ್ಚಿನ ಸೆಟ್ಟಿಂಗ್ಗಳು"</string> <string name="quick_settings_done" msgid="2163641301648855793">"ಮುಗಿದಿದೆ"</string> @@ -879,20 +879,13 @@ </string-array> <string name="tuner_low_priority" msgid="8412666814123009820">"ಕಡಿಮೆ-ಆದ್ಯತೆ ಸೂಚನೆಯ ಐಕಾನ್ಗಳನ್ನು ತೋರಿಸಿ"</string> <string name="other" msgid="429768510980739978">"ಇತರ"</string> - <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) --> - <skip /> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"ಟೈಲ್ ಅನ್ನು ತೆಗೆದುಹಾಕಿ"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"ಕೊನೆಯಲ್ಲಿ ಟೈಲ್ ಸೇರಿಸಿ"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"ಟೈಲ್ ಸರಿಸಿ"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"ಟೈಲ್ ಸೇರಿಸಿ"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"ಇಲ್ಲಿಗೆ ಸರಿಸಿ <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g> ಸ್ಥಾನಕ್ಕೆ ಸೇರಿಸಿ"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"ಸ್ಥಾನ <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"ತ್ವರಿತ ಸೆಟ್ಟಿಂಗ್ಗಳ ಎಡಿಟರ್."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> ಅಧಿಸೂಚನೆ: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"ಸೆಟ್ಟಿಂಗ್ಗಳನ್ನು ತೆರೆಯಿರಿ."</string> diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml index 6a755e52f058..ac7f04e77586 100644 --- a/packages/SystemUI/res/values-ko/strings.xml +++ b/packages/SystemUI/res/values-ko/strings.xml @@ -371,7 +371,7 @@ <string name="quick_settings_time_label" msgid="3352680970557509303">"시간"</string> <string name="quick_settings_user_label" msgid="1253515509432672496">"나"</string> <string name="quick_settings_user_title" msgid="8673045967216204537">"사용자"</string> - <string name="quick_settings_user_new_user" msgid="3347905871336069666">"새 사용자"</string> + <string name="quick_settings_user_new_user" msgid="3347905871336069666">"신규 사용자"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"연결되어 있지 않음"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"네트워크가 연결되지 않음"</string> @@ -473,7 +473,7 @@ <string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"현재 사용자: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"프로필 표시"</string> <string name="user_add_user" msgid="4336657383006913022">"사용자 추가"</string> - <string name="user_new_user_name" msgid="2019166282704195789">"새 사용자"</string> + <string name="user_new_user_name" msgid="2019166282704195789">"신규 사용자"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"게스트를 삭제하시겠습니까?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"이 세션에 있는 모든 앱과 데이터가 삭제됩니다."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"삭제"</string> @@ -879,20 +879,13 @@ </string-array> <string name="tuner_low_priority" msgid="8412666814123009820">"우선순위가 낮은 알림 아이콘 표시"</string> <string name="other" msgid="429768510980739978">"기타"</string> - <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) --> - <skip /> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"타일 삭제"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"끝에 타일 추가"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"타일 이동"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"타일 추가"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g> 위치로 이동"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g> 위치에 추가"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"<xliff:g id="POSITION">%1$d</xliff:g> 위치"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"빠른 설정 편집기"</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> 알림: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"설정 열기"</string> diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml index a740d07f7eac..d775823b298c 100644 --- a/packages/SystemUI/res/values-ky/strings.xml +++ b/packages/SystemUI/res/values-ky/strings.xml @@ -862,7 +862,7 @@ <string name="left_icon" msgid="5036278531966897006">"¨Солго¨ сүрөтчөсү"</string> <string name="right_icon" msgid="1103955040645237425">"¨Оңго¨ сүрөтчөсү"</string> <string name="drag_to_add_tiles" msgid="8933270127508303672">"Керектүү элементтерди сүйрөп келиңиз"</string> - <string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Элементтердин иретин өзгөртүү үчүн, кармап туруп, сүйрөңүз"</string> + <string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Элементтердин иретин өзгөртүү үчүн кармап туруп, сүйрөңүз"</string> <string name="drag_to_remove_tiles" msgid="4682194717573850385">"Алып салуу үчүн бул жерге сүйрөңүз"</string> <string name="drag_to_remove_disabled" msgid="933046987838658850">"Сизге жок дегенде <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> мозаика керек"</string> <string name="qs_edit" msgid="5583565172803472437">"Түзөтүү"</string> @@ -879,20 +879,13 @@ </string-array> <string name="tuner_low_priority" msgid="8412666814123009820">"Анча маанилүү эмес билдирменин сүрөтчөлөрүн көрсөтүү"</string> <string name="other" msgid="429768510980739978">"Башка"</string> - <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) --> - <skip /> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"плитканы өчүрүү"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"плитканы аягына кошуу"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Плитканы жылдыруу"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Плитка кошуу"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Төмөнкүгө жылдыруу: <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g>-позицияга кошуу"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"<xliff:g id="POSITION">%1$d</xliff:g>-позиция"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Ыкчам жөндөөлөр түзөткүчү."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> эскертмеси: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Жөндөөлөрдү ачуу."</string> diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml index 1fb2ca0b0bd2..67642d3471f5 100644 --- a/packages/SystemUI/res/values-lo/strings.xml +++ b/packages/SystemUI/res/values-lo/strings.xml @@ -879,20 +879,13 @@ </string-array> <string name="tuner_low_priority" msgid="8412666814123009820">"ສະແດງໄອຄອນການແຈ້ງເຕືອນຄວາມສຳຄັນຕ່ຳ"</string> <string name="other" msgid="429768510980739978">"ອື່ນໆ"</string> - <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) --> - <skip /> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"ລຶບແຜ່ນອອກ"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"ເພີ່ມແຜ່ນໃສ່ທ້າຍ"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"ຍ້າຍແຜ່ນ"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"ເພີ່ມແຜ່ນ"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"ຍ້າຍໄປ <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"ເພີ່ມໃສ່ຕຳແໜ່ງ <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"ຕຳແໜ່ງ <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"ຕົວແກ້ໄຂການຕັ້ງຄ່າດ່ວນ"</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"ການແຈ້ງເຕືອນ <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"ເປີດການຕັ້ງຄ່າ."</string> diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml index bd24722436dd..e17f6e490373 100644 --- a/packages/SystemUI/res/values-lt/strings.xml +++ b/packages/SystemUI/res/values-lt/strings.xml @@ -889,20 +889,13 @@ </string-array> <string name="tuner_low_priority" msgid="8412666814123009820">"Rodyti mažo prioriteto pranešimų piktogramas"</string> <string name="other" msgid="429768510980739978">"Kita"</string> - <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) --> - <skip /> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"pašalintumėte išklotinės elementą"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"pridėtumėte išklotinės elementą gale"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Perkelti išklotinės elementą"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Pridėti išklotinės elementą"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Perkelkite į <xliff:g id="POSITION">%1$d</xliff:g> poziciją"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Pridėkite <xliff:g id="POSITION">%1$d</xliff:g> pozicijoje"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"<xliff:g id="POSITION">%1$d</xliff:g> pozicija"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Sparčiųjų nustatymų redagavimo priemonė."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"„<xliff:g id="ID_1">%1$s</xliff:g>“ pranešimas: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Atidaryti nustatymus."</string> diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml index e8d9a4d8c797..96d563c61a75 100644 --- a/packages/SystemUI/res/values-lv/strings.xml +++ b/packages/SystemUI/res/values-lv/strings.xml @@ -884,20 +884,13 @@ </string-array> <string name="tuner_low_priority" msgid="8412666814123009820">"Rādīt zemas prioritātes paziņojumu ikonas"</string> <string name="other" msgid="429768510980739978">"Citi"</string> - <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) --> - <skip /> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"noņemt elementu"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"pievienot elementu beigās"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Pārvietot elementu"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Pievienot elementu"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Pārvietot uz pozīciju numur <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Pievienot elementu pozīcijā numur <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Pozīcija numur <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Ātro iestatījumu redaktors."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> paziņojums: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Atvērt iestatījumus."</string> diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml index 164cb96bf046..f53147e03c84 100644 --- a/packages/SystemUI/res/values-mk/strings.xml +++ b/packages/SystemUI/res/values-mk/strings.xml @@ -879,20 +879,13 @@ </string-array> <string name="tuner_low_priority" msgid="8412666814123009820">"Прикажувај икони за известувања со низок приоритет"</string> <string name="other" msgid="429768510980739978">"Друго"</string> - <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) --> - <skip /> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"отстранување на плочката"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"додавање на плочката на крај"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Преместување на плочката"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Додавање плочка"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Преместување на <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Додавање на позиција <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Позиција <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Уредник за брзи поставки."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Известување од <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Отворете ги поставките."</string> diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml index ba379d3b7116..fc6868152dc0 100644 --- a/packages/SystemUI/res/values-ml/strings.xml +++ b/packages/SystemUI/res/values-ml/strings.xml @@ -387,7 +387,7 @@ <string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"വൈഫൈ കണക്റ്റ് ചെയ്തിട്ടില്ല"</string> <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"തെളിച്ചം"</string> <string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="2325362583903258677">"യാന്ത്രികം"</string> - <string name="quick_settings_inversion_label" msgid="5078769633069667698">"നിറം മാറ്റുക"</string> + <string name="quick_settings_inversion_label" msgid="5078769633069667698">"നെഗറ്റീവ് ലുക്ക്"</string> <string name="quick_settings_color_space_label" msgid="537528291083575559">"വർണ്ണം ശരിയാക്കൽ മോഡ്"</string> <string name="quick_settings_more_settings" msgid="2878235926753776694">"കൂടുതൽ ക്രമീകരണങ്ങൾ"</string> <string name="quick_settings_done" msgid="2163641301648855793">"പൂർത്തിയാക്കി"</string> @@ -879,20 +879,13 @@ </string-array> <string name="tuner_low_priority" msgid="8412666814123009820">"പ്രാധാന്യം കുറഞ്ഞ അറിയിപ്പ് ചിഹ്നങ്ങൾ"</string> <string name="other" msgid="429768510980739978">"മറ്റുള്ളവ"</string> - <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) --> - <skip /> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"ടൈൽ നീക്കം ചെയ്യുക"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"ടൈൽ, അവസാന ഭാഗത്ത് ചേർക്കുക"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"ടൈൽ നീക്കുക"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"ടൈൽ ചേർക്കുക"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"സ്ഥാനം <xliff:g id="POSITION">%1$d</xliff:g>-ലേക്ക് നീക്കുക"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"സ്ഥാനം <xliff:g id="POSITION">%1$d</xliff:g>-ൽ ചേർക്കുക"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"സ്ഥാനം <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"ദ്രുത ക്രമീകരണ എഡിറ്റർ."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> അറിയിപ്പ്: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"ക്രമീകരണം തുറക്കുക."</string> diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml index 56c5ef27baec..1c82e48c9ad9 100644 --- a/packages/SystemUI/res/values-mn/strings.xml +++ b/packages/SystemUI/res/values-mn/strings.xml @@ -879,20 +879,13 @@ </string-array> <string name="tuner_low_priority" msgid="8412666814123009820">"Бага ач холбогдолтой мэдэгдлийн дүрс тэмдгийг харуулах"</string> <string name="other" msgid="429768510980739978">"Бусад"</string> - <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) --> - <skip /> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"хавтанг хасна уу"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"дуусгахын тулд хавтан нэмэх"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Хавтанг зөөх"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Хавтан нэмэх"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g> руу зөөнө үү"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g> байрлалд нэмнэ үү"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"<xliff:g id="POSITION">%1$d</xliff:g> байрлал"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Түргэн тохиргоо засварлагч."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> мэдэгдэл: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Тохиргоог нээнэ үү."</string> diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml index 728065fbd5cc..b26f89f63243 100644 --- a/packages/SystemUI/res/values-mr/strings.xml +++ b/packages/SystemUI/res/values-mr/strings.xml @@ -879,20 +879,13 @@ </string-array> <string name="tuner_low_priority" msgid="8412666814123009820">"कमी प्राधान्य सूचना आयकन दर्शवा"</string> <string name="other" msgid="429768510980739978">"अन्य"</string> - <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) --> - <skip /> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"टाइल काढून टाका"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"टाइल शेवटच्या स्थानावर जोडा"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"टाइल हलवा"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"टाइल जोडा"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g> यावर हलवा"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g> स्थानावर जोडा"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"स्थान <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"द्रुत सेटिंग्ज संपादक."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> सूचना: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"सेटिंग्ज उघडा."</string> diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml index 62d845c3f72a..92bc807e76cc 100644 --- a/packages/SystemUI/res/values-ms/strings.xml +++ b/packages/SystemUI/res/values-ms/strings.xml @@ -879,20 +879,13 @@ </string-array> <string name="tuner_low_priority" msgid="8412666814123009820">"Tunjukkan ikon pemberitahuan keutamaan rendah"</string> <string name="other" msgid="429768510980739978">"Lain-lain"</string> - <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) --> - <skip /> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"alih keluar jubin"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"tambahkan jubin pada bahagian hujung"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Alihkan jubin"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Tambahkan jubin"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Alih ke <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Tambahkan pada kedudukan <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Kedudukan <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor tetapan pantas."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Pemberitahuan <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Buka tetapan."</string> @@ -975,7 +968,7 @@ <string name="auto_saver_enabled_text" msgid="7889491183116752719">"Penjimat Bateri akan dihidupkan secara automatik setelah kuasa bateri kurang daripada <xliff:g id="PERCENTAGE">%d</xliff:g>%%."</string> <string name="open_saver_setting_action" msgid="2111461909782935190">"Tetapan"</string> <string name="auto_saver_okay_action" msgid="7815925750741935386">"OK"</string> - <string name="heap_dump_tile_name" msgid="2464189856478823046">"Longgok Tmbunn SysUI"</string> + <string name="heap_dump_tile_name" msgid="2464189856478823046">"DumpSys"</string> <string name="ongoing_privacy_chip_content_single_app" msgid="2969750601815230385">"<xliff:g id="APP">%1$s</xliff:g> sedang menggunakan <xliff:g id="TYPES_LIST">%2$s</xliff:g> anda."</string> <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Aplikasi sedang menggunakan <xliff:g id="TYPES_LIST">%s</xliff:g> anda."</string> <string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string> diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml index e72f16925eab..9cc9398979de 100644 --- a/packages/SystemUI/res/values-my/strings.xml +++ b/packages/SystemUI/res/values-my/strings.xml @@ -879,20 +879,13 @@ </string-array> <string name="tuner_low_priority" msgid="8412666814123009820">"အရေးမကြီးသော အကြောင်းကြားချက် သင်္ကေတများ ပြရန်"</string> <string name="other" msgid="429768510980739978">"အခြား"</string> - <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) --> - <skip /> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"အကွက်ငယ်ကို ဖယ်ရှားရန်"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"အဆုံးတွင် အကွက်ငယ်ထည့်ရန်"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"အကွက်ငယ်ကို ရွှေ့ရန်"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"အကွက်ငယ်ကို ထည့်ရန်"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g> သို့ ရွှေ့ရန်"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g> အနေအထားသို့ ပေါင်းထည့်ရန်"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"<xliff:g id="POSITION">%1$d</xliff:g> အနေအထား"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"မြန်ဆန်သည့် ဆက်တင်တည်းဖြတ်စနစ်"</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> အကြောင်းကြားချက် − <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"ဆက်တင်များကို ဖွင့်ပါ။"</string> diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml index 41aabcb89b65..92ae68943352 100644 --- a/packages/SystemUI/res/values-nb/strings.xml +++ b/packages/SystemUI/res/values-nb/strings.xml @@ -879,20 +879,13 @@ </string-array> <string name="tuner_low_priority" msgid="8412666814123009820">"Vis ikoner for varsler med lav prioritet"</string> <string name="other" msgid="429768510980739978">"Annet"</string> - <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) --> - <skip /> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"fjerne infobrikken"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"legge til en infobrikke på slutten"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Flytt infobrikken"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Legg til en infobrikke"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Flytt til <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Legg til posisjonen <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Posisjon <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Redigeringsvindu for hurtiginnstillinger."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g>-varsel: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Åpne innstillingene."</string> diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml index 0cf09d2bdd0d..1346874ed08f 100644 --- a/packages/SystemUI/res/values-ne/strings.xml +++ b/packages/SystemUI/res/values-ne/strings.xml @@ -879,20 +879,13 @@ </string-array> <string name="tuner_low_priority" msgid="8412666814123009820">"कम प्राथमिकताका सूचना आइकनहरू देखाउनुहोस्"</string> <string name="other" msgid="429768510980739978">"अन्य"</string> - <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) --> - <skip /> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"टाइल हटाउनुहोस्"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"टाइल अन्त्यमा हाल्नुहोस्"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"टाइल सार्नुहोस्"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"टाइल हाल्नुहोस्"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"टाइल सारेर <xliff:g id="POSITION">%1$d</xliff:g> मा लैजानुहोस्"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"टाइल यो अवस्था <xliff:g id="POSITION">%1$d</xliff:g> मा हाल्नुहोस्"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"स्थिति <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"द्रुत सेटिङ सम्पादक।"</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> को सूचना: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"सेटिङहरूलाई खोल्नुहोस्।"</string> diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml index 49e097e5a047..139b57b7c3bf 100644 --- a/packages/SystemUI/res/values-nl/strings.xml +++ b/packages/SystemUI/res/values-nl/strings.xml @@ -879,20 +879,13 @@ </string-array> <string name="tuner_low_priority" msgid="8412666814123009820">"Pictogrammen voor meldingen met lage prioriteit weergeven"</string> <string name="other" msgid="429768510980739978">"Overig"</string> - <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) --> - <skip /> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"tegel verwijderen"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"tegel toevoegen aan einde"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Tegel verplaatsen"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Tegel toevoegen"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Verplaatsen naar <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Toevoegen aan positie <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Positie <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor voor \'Snelle instellingen\'."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g>-melding: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Instellingen openen."</string> diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml index c3105ff55bc7..3973734f16d2 100644 --- a/packages/SystemUI/res/values-or/strings.xml +++ b/packages/SystemUI/res/values-or/strings.xml @@ -861,7 +861,7 @@ <string name="right_keycode" msgid="2480715509844798438">"ଡାହାଣ କୀ\'କୋଡ୍"</string> <string name="left_icon" msgid="5036278531966897006">"ବାମ ଆଇକନ୍"</string> <string name="right_icon" msgid="1103955040645237425">"ଡାହାଣ ଆଇକନ୍"</string> - <string name="drag_to_add_tiles" msgid="8933270127508303672">"ଟାଇଲ୍ ଯୋଡ଼ିବା ପାଇଁ ଦାବିଧରି ଟାଣନ୍ତୁ"</string> + <string name="drag_to_add_tiles" msgid="8933270127508303672">"ଟାଇଲ୍ ଯୋଗ କରିବା ପାଇଁ ଦାବିଧରି ଟାଣନ୍ତୁ"</string> <string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"ଟାଇଲ୍ ପୁଣି ସଜାଇବାକୁ ଦାବିଧରି ଟାଣନ୍ତୁ"</string> <string name="drag_to_remove_tiles" msgid="4682194717573850385">"ବାହାର କରିବାକୁ ଏଠାକୁ ଡ୍ରାଗ୍ କରନ୍ତୁ"</string> <string name="drag_to_remove_disabled" msgid="933046987838658850">"ଆପଣଙ୍କର ଅତିକମ୍ରେ <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g>ଟି ଟାଇଲ୍ ଆବଶ୍ୟକ"</string> @@ -879,20 +879,13 @@ </string-array> <string name="tuner_low_priority" msgid="8412666814123009820">"କମ୍-ଅଗ୍ରାଧିକାର ବିଜ୍ଞପ୍ତି ଆଇକନ୍ ଦେଖାନ୍ତୁ"</string> <string name="other" msgid="429768510980739978">"ଅନ୍ୟାନ୍ୟ"</string> - <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) --> - <skip /> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"ଟାଇଲ୍ କାଢ଼ି ଦିଅନ୍ତୁ"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"ଶେଷରେ ଟାଇଲ୍ ଯୋଗ କରନ୍ତୁ"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"ଟାଇଲ୍ ମୁଭ୍ କରନ୍ତୁ"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"ଟାଇଲ୍ ଯୋଗ କରନ୍ତୁ"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g>କୁ ମୁଭ୍ କରନ୍ତୁ"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g> ଅବସ୍ଥିତିରେ ଯୋଗ କରନ୍ତୁ"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"ଅବସ୍ଥିତି <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"ଦ୍ରୁତ ସେଟିଙ୍ଗ ଏଡିଟର୍।"</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> ବିଜ୍ଞପ୍ତି: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"ସେଟିଂସ୍ ଖୋଲନ୍ତୁ।"</string> diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml index b12dfb3954ee..e727f48a1a3e 100644 --- a/packages/SystemUI/res/values-pa/strings.xml +++ b/packages/SystemUI/res/values-pa/strings.xml @@ -879,20 +879,13 @@ </string-array> <string name="tuner_low_priority" msgid="8412666814123009820">"ਘੱਟ ਤਰਜੀਹ ਵਾਲੇ ਸੂਚਨਾ ਪ੍ਰਤੀਕਾਂ ਨੂੰ ਦਿਖਾਓ"</string> <string name="other" msgid="429768510980739978">"ਹੋਰ"</string> - <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) --> - <skip /> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"ਟਾਇਲ ਹਟਾਓ"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"ਟਾਇਲ ਨੂੰ ਅੰਤ ਵਿੱਚ ਸ਼ਾਮਲ ਕਰੋ"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"ਟਾਇਲ ਨੂੰ ਲਿਜਾਓ"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"ਟਾਇਲ ਸ਼ਾਮਲ ਕਰੋ"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g> \'ਤੇ ਲਿਜਾਓ"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g> ਸਥਾਨ \'ਤੇ ਸ਼ਾਮਲ ਕਰੋ"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"ਸਥਾਨ <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"ਤਤਕਾਲ ਸੈਟਿੰਗਾਂ ਸੰਪਾਦਕ।"</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> ਸੂਚਨਾ: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"ਸੈਟਿੰਗਾਂ ਖੋਲ੍ਹੋ।"</string> diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml index d195977f460b..f188aaa1ca76 100644 --- a/packages/SystemUI/res/values-pl/strings.xml +++ b/packages/SystemUI/res/values-pl/strings.xml @@ -889,20 +889,13 @@ </string-array> <string name="tuner_low_priority" msgid="8412666814123009820">"Pokazuj ikony powiadomień o niskim priorytecie"</string> <string name="other" msgid="429768510980739978">"Inne"</string> - <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) --> - <skip /> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"usunąć kartę"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"dodać kartę na końcu"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Przenieś kartę"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Dodaj kartę"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Przenieś do pozycji <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Dodaj w pozycji <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Pozycja <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Edytor szybkich ustawień."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Powiadomienie z aplikacji <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Otwórz ustawienia."</string> diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml index 6f96f37bc302..8c8adae62e82 100644 --- a/packages/SystemUI/res/values-pt-rBR/strings.xml +++ b/packages/SystemUI/res/values-pt-rBR/strings.xml @@ -879,20 +879,13 @@ </string-array> <string name="tuner_low_priority" msgid="8412666814123009820">"Mostrar ícones de notificações de baixa prioridade"</string> <string name="other" msgid="429768510980739978">"Outros"</string> - <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) --> - <skip /> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"remover o bloco"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"adicionar o bloco ao final"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Mover bloco"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Adicionar bloco"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Mover para <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Adicionar à posição <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Posição <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor de configurações rápidas."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Notificação do <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Abrir configurações."</string> diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml index 93999f6b7b49..082e14ec6fc3 100644 --- a/packages/SystemUI/res/values-pt-rPT/strings.xml +++ b/packages/SystemUI/res/values-pt-rPT/strings.xml @@ -861,7 +861,7 @@ <string name="right_keycode" msgid="2480715509844798438">"Código de tecla direito"</string> <string name="left_icon" msgid="5036278531966897006">"Ícone esquerdo"</string> <string name="right_icon" msgid="1103955040645237425">"Ícone direito"</string> - <string name="drag_to_add_tiles" msgid="8933270127508303672">"Toque sem soltar e arraste para adicionar mosaicos."</string> + <string name="drag_to_add_tiles" msgid="8933270127508303672">"Tocar sem soltar e arrastar para adicionar mosaicos"</string> <string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Tocar sem soltar e arrastar para reorganizar os mosaicos"</string> <string name="drag_to_remove_tiles" msgid="4682194717573850385">"Arrastar para aqui para remover"</string> <string name="drag_to_remove_disabled" msgid="933046987838658850">"Necessita de, pelo menos, <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> cartões"</string> @@ -879,20 +879,13 @@ </string-array> <string name="tuner_low_priority" msgid="8412666814123009820">"Mostrar ícones de notificações de prioridade baixa"</string> <string name="other" msgid="429768510980739978">"Outro"</string> - <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) --> - <skip /> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"remover o cartão"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"adicionar o cartão ao final"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Mover cartão"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Adicionar cartão"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Mova para <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Adicione à posição <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Posição <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor de definições rápidas."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Notificação do <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Abrir as definições."</string> diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml index 6f96f37bc302..8c8adae62e82 100644 --- a/packages/SystemUI/res/values-pt/strings.xml +++ b/packages/SystemUI/res/values-pt/strings.xml @@ -879,20 +879,13 @@ </string-array> <string name="tuner_low_priority" msgid="8412666814123009820">"Mostrar ícones de notificações de baixa prioridade"</string> <string name="other" msgid="429768510980739978">"Outros"</string> - <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) --> - <skip /> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"remover o bloco"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"adicionar o bloco ao final"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Mover bloco"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Adicionar bloco"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Mover para <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Adicionar à posição <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Posição <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor de configurações rápidas."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Notificação do <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Abrir configurações."</string> diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml index 9810cc5540cb..4128c21eaa4e 100644 --- a/packages/SystemUI/res/values-ro/strings.xml +++ b/packages/SystemUI/res/values-ro/strings.xml @@ -884,20 +884,13 @@ </string-array> <string name="tuner_low_priority" msgid="8412666814123009820">"Afișați pictogramele de notificare cu prioritate redusă"</string> <string name="other" msgid="429768510980739978">"Altele"</string> - <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) --> - <skip /> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"eliminați cardul"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"adăugați cardul la sfârșit"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Mutați cardul"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Adăugați un card"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Mutați pe poziția <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Adăugați pe poziția <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Poziția <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editorul pentru setări rapide."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Notificare <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Deschideți setările."</string> diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml index 9f43b5ac7f16..66e3ec3ba78a 100644 --- a/packages/SystemUI/res/values-ru/strings.xml +++ b/packages/SystemUI/res/values-ru/strings.xml @@ -889,20 +889,13 @@ </string-array> <string name="tuner_low_priority" msgid="8412666814123009820">"Показывать значки уведомлений с низким приоритетом"</string> <string name="other" msgid="429768510980739978">"Другое"</string> - <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) --> - <skip /> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"удалить кнопку быстрого доступа"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"добавить кнопку быстрого доступа в конец"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Переместить кнопку быстрого доступа"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Добавить кнопку быстрого доступа"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Переместить на позицию <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Добавить на позицию <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Позиция <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Редактор быстрых настроек."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Уведомление <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Открыть настройки."</string> diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml index 700f5101475c..98e8d1fd9d63 100644 --- a/packages/SystemUI/res/values-si/strings.xml +++ b/packages/SystemUI/res/values-si/strings.xml @@ -879,20 +879,13 @@ </string-array> <string name="tuner_low_priority" msgid="8412666814123009820">"අඩු ප්රමුඛතා දැනුම්දීම් අයිකන පෙන්වන්න"</string> <string name="other" msgid="429768510980739978">"වෙනත්"</string> - <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) --> - <skip /> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"ටයිල් ඉවත් කරන්න"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"අගට ටයිල් එක් කරන්න"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"ටයිල් ගෙන යන්න"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"ටයිල් එක් කරන්න"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g> වෙත ගෙන යන්න"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g> ස්ථානයට එක් කරන්න"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"ස්ථානය <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"ඉක්මන් සැකසුම් සංස්කාරකය."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> දැනුම්දීම: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"සැකසීම් විවෘත කරන්න."</string> diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml index 62df35aeee02..d9c6279ec42e 100644 --- a/packages/SystemUI/res/values-sk/strings.xml +++ b/packages/SystemUI/res/values-sk/strings.xml @@ -889,20 +889,13 @@ </string-array> <string name="tuner_low_priority" msgid="8412666814123009820">"Zobraziť ikony upozornení s nízkou prioritou"</string> <string name="other" msgid="429768510980739978">"Ďalšie"</string> - <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) --> - <skip /> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"odstrániť kartu"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"pridať kartu na koniec"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Presunúť kartu"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Pridať kartu"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Presunúť na <xliff:g id="POSITION">%1$d</xliff:g>. pozíciu"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Pridať na <xliff:g id="POSITION">%1$d</xliff:g>. pozíciu"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"<xliff:g id="POSITION">%1$d</xliff:g>. pozícia"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor rýchlych nastavení"</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Upozornenie <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Otvoriť nastavenia"</string> diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml index 25bdd656b56c..778938c77374 100644 --- a/packages/SystemUI/res/values-sl/strings.xml +++ b/packages/SystemUI/res/values-sl/strings.xml @@ -889,20 +889,13 @@ </string-array> <string name="tuner_low_priority" msgid="8412666814123009820">"Pokaži ikone obvestil z nizko stopnjo prednosti"</string> <string name="other" msgid="429768510980739978">"Drugo"</string> - <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) --> - <skip /> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"odstranitev ploščice"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"dodajanje ploščice na konec"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Premik ploščice"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Dodajanje ploščice"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Premik na položaj <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Dodajanje na položaj <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Položaj <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Urejevalnik hitrih nastavitev."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Obvestilo za <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Odpri nastavitve."</string> diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml index e117602afa9a..90f1c5adf7be 100644 --- a/packages/SystemUI/res/values-sr/strings.xml +++ b/packages/SystemUI/res/values-sr/strings.xml @@ -884,20 +884,13 @@ </string-array> <string name="tuner_low_priority" msgid="8412666814123009820">"Прикажи иконе обавештења ниског приоритета"</string> <string name="other" msgid="429768510980739978">"Друго"</string> - <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) --> - <skip /> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"уклонили плочицу"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"додали плочицу на крај"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Преместите плочицу"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Додајте плочицу"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Преместите на <xliff:g id="POSITION">%1$d</xliff:g>. позицију"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Додајте на <xliff:g id="POSITION">%1$d</xliff:g>. позицију"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"<xliff:g id="POSITION">%1$d</xliff:g>. позиција"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Уређивач за Брза подешавања."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Обавештења за <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Отвори Подешавања."</string> diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml index 8559b0b0de2b..8918f519b020 100644 --- a/packages/SystemUI/res/values-sv/strings.xml +++ b/packages/SystemUI/res/values-sv/strings.xml @@ -879,20 +879,13 @@ </string-array> <string name="tuner_low_priority" msgid="8412666814123009820">"Visa ikoner för aviseringar med låg prioritet"</string> <string name="other" msgid="429768510980739978">"Annat"</string> - <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) --> - <skip /> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"ta bort ruta"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"lägg till ruta i slutet"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Flytta ruta"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Lägg till ruta"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Flytta till <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Lägg till på position <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Position <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Redigerare för snabbinställningar."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g>-avisering: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Öppna inställningarna."</string> diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml index 76b86eee8289..3949f2df6e40 100644 --- a/packages/SystemUI/res/values-sw/strings.xml +++ b/packages/SystemUI/res/values-sw/strings.xml @@ -879,20 +879,13 @@ </string-array> <string name="tuner_low_priority" msgid="8412666814123009820">"Onyesha aikoni za arifa zisizo muhimu"</string> <string name="other" msgid="429768510980739978">"Nyingine"</string> - <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) --> - <skip /> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"ondoa kigae"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"ongeza kigae mwishoni"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Hamisha kigae"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Ongeza kigae"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Hamishia kwenye <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Ongeza kwenye nafasi ya <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Nafasi ya <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Kihariri cha Mipangilio ya haraka."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Arifa kutoka <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Fungua mipangilio."</string> diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml index 925c958199ed..d7a27b86a99a 100644 --- a/packages/SystemUI/res/values-te/strings.xml +++ b/packages/SystemUI/res/values-te/strings.xml @@ -879,20 +879,13 @@ </string-array> <string name="tuner_low_priority" msgid="8412666814123009820">"తక్కువ ప్రాధాన్యత నోటిఫికేషన్ చిహ్నాలను చూపించు"</string> <string name="other" msgid="429768510980739978">"ఇతరం"</string> - <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) --> - <skip /> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"టైల్ను తీసివేయండి"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"ముగించడానికి టైల్ను జోడించండి"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"టైల్ను తరలించండి"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"టైల్ను జోడించండి"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g>కు తరలించండి"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g> స్థానానికి జోడించండి"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"స్థానం <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"శీఘ్ర సెట్టింగ్ల ఎడిటర్."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> నోటిఫికేషన్: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"సెట్టింగ్లను తెరవండి."</string> diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml index c613e9c0d7c0..cb6089fa0344 100644 --- a/packages/SystemUI/res/values-th/strings.xml +++ b/packages/SystemUI/res/values-th/strings.xml @@ -879,20 +879,13 @@ </string-array> <string name="tuner_low_priority" msgid="8412666814123009820">"แสดงไอคอนการแจ้งเตือนลำดับความสำคัญต่ำ"</string> <string name="other" msgid="429768510980739978">"อื่นๆ"</string> - <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) --> - <skip /> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"นำชิ้นส่วนออก"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"เพิ่มชิ้นส่วนต่อท้าย"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"ย้ายชิ้นส่วน"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"เพิ่มชิ้นส่วน"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"ย้ายไปที่ <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"เพิ่มไปยังตำแหน่ง <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"ตำแหน่ง <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"ตัวแก้ไขการตั้งค่าด่วน"</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> การแจ้งเตือน: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"เปิดการตั้งค่า"</string> diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml index 9411957e56f2..7e6aa629ea63 100644 --- a/packages/SystemUI/res/values-tl/strings.xml +++ b/packages/SystemUI/res/values-tl/strings.xml @@ -879,20 +879,13 @@ </string-array> <string name="tuner_low_priority" msgid="8412666814123009820">"Ipakita ang mga icon ng notification na may mababang priority"</string> <string name="other" msgid="429768510980739978">"Iba pa"</string> - <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) --> - <skip /> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"alisin ang tile"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"idagdag ang tile sa dulo"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Ilipat ang tile"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Magdagdag ng tile"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Ilipat sa <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Idagdag sa posisyong <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Posisyon <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor ng Mga mabilisang setting."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Notification sa <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Buksan ang mga setting."</string> diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml index 4d46f93e2d25..dac352f79d8b 100644 --- a/packages/SystemUI/res/values-tr/strings.xml +++ b/packages/SystemUI/res/values-tr/strings.xml @@ -879,20 +879,13 @@ </string-array> <string name="tuner_low_priority" msgid="8412666814123009820">"Düşük öncelikli bildirim simgelerini göster"</string> <string name="other" msgid="429768510980739978">"Diğer"</string> - <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) --> - <skip /> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"Karoyu kaldırmak için"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"Sona karo eklemek için"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Karoyu taşı"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Karo ekle"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g> konumuna taşı"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g> konumuna ekle"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Konum: <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Hızlı ayar düzenleyicisi."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> bildirimi: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Ayarları aç."</string> diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml index 399b53a91de6..eeedac603ab5 100644 --- a/packages/SystemUI/res/values-uk/strings.xml +++ b/packages/SystemUI/res/values-uk/strings.xml @@ -889,20 +889,13 @@ </string-array> <string name="tuner_low_priority" msgid="8412666814123009820">"Показувати значки сповіщень із низьким пріоритетом"</string> <string name="other" msgid="429768510980739978">"Інше"</string> - <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) --> - <skip /> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"вилучити значок"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"додати значок у кінець"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Перемістити значок"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Додати значок"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Перемістити на позицію <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Додати на позицію <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Позиція <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Редактор швидких налаштувань."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Сповіщення <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Відкрити налаштування."</string> diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml index 09e500d227a6..4bd192fd63f0 100644 --- a/packages/SystemUI/res/values-ur/strings.xml +++ b/packages/SystemUI/res/values-ur/strings.xml @@ -879,20 +879,13 @@ </string-array> <string name="tuner_low_priority" msgid="8412666814123009820">"کم ترجیحی اطلاع کے آئیکنز دکھائیں"</string> <string name="other" msgid="429768510980739978">"دیگر"</string> - <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) --> - <skip /> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"ٹائل ہٹائیں"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"ختم کرنے کے لیے ٹائل شامل کریں"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"ٹائل منتقل کریں"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"ٹائل شامل کریں"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g> میں منتقل کریں"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"پوزیشن <xliff:g id="POSITION">%1$d</xliff:g> میں شامل کریں"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"پوزیشن <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"فوری ترتیبات کا ایڈیٹر۔"</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> اطلاع: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"ترتیبات کھولیں۔"</string> diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml index 61b91c768582..7c3728abab78 100644 --- a/packages/SystemUI/res/values-uz/strings.xml +++ b/packages/SystemUI/res/values-uz/strings.xml @@ -879,20 +879,13 @@ </string-array> <string name="tuner_low_priority" msgid="8412666814123009820">"Muhim boʻlmagan bildirishnoma ikonkalarini koʻrsatish"</string> <string name="other" msgid="429768510980739978">"Boshqa"</string> - <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) --> - <skip /> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"katakchani olib tashlash"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"oxiriga katakcha kiritish"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Katakchani boshqa joyga olish"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Katakcha kiritish"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Bu joyga olish: <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Bu joyga kiritish: <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Joylashuv: <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Tezkor sozlamalar muharriri"</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> bildirishnomasi: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Sozlamalarni ochish."</string> diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml index dc2a5b826439..f23f7424a073 100644 --- a/packages/SystemUI/res/values-vi/strings.xml +++ b/packages/SystemUI/res/values-vi/strings.xml @@ -879,20 +879,13 @@ </string-array> <string name="tuner_low_priority" msgid="8412666814123009820">"Hiển thị biểu tượng thông báo có mức ưu tiên thấp"</string> <string name="other" msgid="429768510980739978">"Khác"</string> - <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) --> - <skip /> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"xóa ô"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"thêm ô vào cuối"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Di chuyển ô"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Thêm ô"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Di chuyển tới <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Thêm vào vị trí <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Vị trí <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Trình chỉnh sửa cài đặt nhanh."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Thông báo của <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Mở phần cài đặt."</string> diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml index 71a7d8e7165d..9b81cb04db1a 100644 --- a/packages/SystemUI/res/values-zh-rCN/strings.xml +++ b/packages/SystemUI/res/values-zh-rCN/strings.xml @@ -879,20 +879,13 @@ </string-array> <string name="tuner_low_priority" msgid="8412666814123009820">"显示低优先级的通知图标"</string> <string name="other" msgid="429768510980739978">"其他"</string> - <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) --> - <skip /> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"移除图块"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"将图块添加到末尾"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"移动图块"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"添加图块"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"移至 <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"添加到位置 <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"位置 <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"快捷设置编辑器。"</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g>通知:<xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"打开设置。"</string> diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml index 1d763d010984..facb47a3bdfc 100644 --- a/packages/SystemUI/res/values-zh-rHK/strings.xml +++ b/packages/SystemUI/res/values-zh-rHK/strings.xml @@ -879,20 +879,13 @@ </string-array> <string name="tuner_low_priority" msgid="8412666814123009820">"顯示低優先順序通知圖示"</string> <string name="other" msgid="429768510980739978">"其他"</string> - <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) --> - <skip /> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"移除圖塊"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"將圖塊加到結尾"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"移圖塊"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"加圖塊"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"移去 <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"加去位置 <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"位置 <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"快速設定編輯工具。"</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> 通知:<xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"開啟設定。"</string> @@ -975,7 +968,7 @@ <string name="auto_saver_enabled_text" msgid="7889491183116752719">"省電模式將會在電量低於 <xliff:g id="PERCENTAGE">%d</xliff:g>%% 時自動開啟。"</string> <string name="open_saver_setting_action" msgid="2111461909782935190">"設定"</string> <string name="auto_saver_okay_action" msgid="7815925750741935386">"知道了"</string> - <string name="heap_dump_tile_name" msgid="2464189856478823046">"轉儲 SysUI 堆"</string> + <string name="heap_dump_tile_name" msgid="2464189856478823046">"Dump SysUI Heap"</string> <string name="ongoing_privacy_chip_content_single_app" msgid="2969750601815230385">"「<xliff:g id="APP">%1$s</xliff:g>」正在使用<xliff:g id="TYPES_LIST">%2$s</xliff:g>。"</string> <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"有多個應用程式正在使用<xliff:g id="TYPES_LIST">%s</xliff:g>。"</string> <string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">"、 "</string> diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml index 303ddd925a86..4e4db70ddd67 100644 --- a/packages/SystemUI/res/values-zh-rTW/strings.xml +++ b/packages/SystemUI/res/values-zh-rTW/strings.xml @@ -879,20 +879,13 @@ </string-array> <string name="tuner_low_priority" msgid="8412666814123009820">"顯示低優先順序通知圖示"</string> <string name="other" msgid="429768510980739978">"其他"</string> - <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) --> - <skip /> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"移除圖塊"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"將圖塊加到結尾處"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"移動圖塊"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"新增圖塊"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"移至 <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"新增到位置 <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"位置 <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"快速設定編輯器。"</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> 通知:<xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"開啟設定。"</string> @@ -975,7 +968,7 @@ <string name="auto_saver_enabled_text" msgid="7889491183116752719">"省電模式會在電量低於 <xliff:g id="PERCENTAGE">%d</xliff:g>%% 時自動開啟。"</string> <string name="open_saver_setting_action" msgid="2111461909782935190">"設定"</string> <string name="auto_saver_okay_action" msgid="7815925750741935386">"我知道了"</string> - <string name="heap_dump_tile_name" msgid="2464189856478823046">"傾印 SysUI 記憶體快照"</string> + <string name="heap_dump_tile_name" msgid="2464189856478823046">"Dump SysUI Heap"</string> <string name="ongoing_privacy_chip_content_single_app" msgid="2969750601815230385">"「<xliff:g id="APP">%1$s</xliff:g>」正在使用<xliff:g id="TYPES_LIST">%2$s</xliff:g>。"</string> <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"有多個應用程式正在使用<xliff:g id="TYPES_LIST">%s</xliff:g>。"</string> <string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">"、 "</string> diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml index d8eb7ade9600..65e9a1c6a5e0 100644 --- a/packages/SystemUI/res/values-zu/strings.xml +++ b/packages/SystemUI/res/values-zu/strings.xml @@ -879,20 +879,13 @@ </string-array> <string name="tuner_low_priority" msgid="8412666814123009820">"Bonisa izithonjana zesaziso zokubaluleka okuncane"</string> <string name="other" msgid="429768510980739978">"Okunye"</string> - <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) --> - <skip /> - <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) --> - <skip /> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"susa ithayela"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"engeza ithayela ekugcineni"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Hambisa ithayela"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Engeza ithayela"</string> + <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Hambisa ku-<xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Engeza kusikhundla se-<xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Isikhundla se-<xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Isihleli sezilungiselelo ezisheshayo."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> isaziso: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Vula izilungiselelo."</string> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 98e8cde40275..d5362baaacf0 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -226,6 +226,8 @@ <dimen name="notification_guts_conversation_action_text_padding_start">32dp</dimen> <dimen name="conversation_onboarding_bullet_gap_width">6dp</dimen> + <dimen name="notification_guts_header_top_padding">11dp</dimen> + <!-- The height of the header in inline settings --> <dimen name="notification_guts_header_height">24dp</dimen> @@ -1368,9 +1370,10 @@ <dimen name="config_rounded_mask_size_bottom">@*android:dimen/rounded_corner_radius_bottom</dimen> <!-- Output switcher panel related dimensions --> - <dimen name="media_output_dialog_padding_top">11dp</dimen> + <dimen name="media_output_dialog_list_margin">12dp</dimen> <dimen name="media_output_dialog_list_max_height">364dp</dimen> <dimen name="media_output_dialog_header_album_icon_size">52dp</dimen> <dimen name="media_output_dialog_header_back_icon_size">36dp</dimen> + <dimen name="media_output_dialog_header_icon_padding">16dp</dimen> <dimen name="media_output_dialog_icon_corner_radius">16dp</dimen> </resources> diff --git a/packages/SystemUI/src/com/android/keyguard/AdminSecondaryLockScreenController.java b/packages/SystemUI/src/com/android/keyguard/AdminSecondaryLockScreenController.java index 23195af8bdea..e99245fa438f 100644 --- a/packages/SystemUI/src/com/android/keyguard/AdminSecondaryLockScreenController.java +++ b/packages/SystemUI/src/com/android/keyguard/AdminSecondaryLockScreenController.java @@ -33,13 +33,9 @@ import android.view.SurfaceView; import android.view.ViewGroup; import com.android.internal.annotations.VisibleForTesting; -import com.android.keyguard.dagger.KeyguardBouncerScope; -import com.android.systemui.dagger.qualifiers.Main; import java.util.NoSuchElementException; -import javax.inject.Inject; - /** * Encapsulates all logic for secondary lockscreen state management. */ @@ -146,9 +142,9 @@ public class AdminSecondaryLockScreenController { } }; - private AdminSecondaryLockScreenController(Context context, KeyguardSecurityContainer parent, + public AdminSecondaryLockScreenController(Context context, ViewGroup parent, KeyguardUpdateMonitor updateMonitor, KeyguardSecurityCallback callback, - @Main Handler handler) { + Handler handler) { mContext = context; mHandler = handler; mParent = parent; @@ -238,26 +234,4 @@ public class AdminSecondaryLockScreenController { getHolder().removeCallback(mSurfaceHolderCallback); } } - - @KeyguardBouncerScope - public static class Factory { - private final Context mContext; - private final KeyguardSecurityContainer mParent; - private final KeyguardUpdateMonitor mUpdateMonitor; - private final Handler mHandler; - - @Inject - public Factory(Context context, KeyguardSecurityContainer parent, - KeyguardUpdateMonitor updateMonitor, @Main Handler handler) { - mContext = context; - mParent = parent; - mUpdateMonitor = updateMonitor; - mHandler = handler; - } - - public AdminSecondaryLockScreenController create(KeyguardSecurityCallback callback) { - return new AdminSecondaryLockScreenController(mContext, mParent, mUpdateMonitor, - callback, mHandler); - } - } } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java index cc6df45c598f..88f4176f5eac 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java @@ -16,26 +16,46 @@ package com.android.keyguard; +import static com.android.internal.util.LatencyTracker.ACTION_CHECK_CREDENTIAL; +import static com.android.internal.util.LatencyTracker.ACTION_CHECK_CREDENTIAL_UNLOCKED; + import android.content.Context; +import android.content.res.ColorStateList; +import android.os.AsyncTask; +import android.os.CountDownTimer; +import android.os.SystemClock; import android.util.AttributeSet; import android.view.HapticFeedbackConstants; import android.view.KeyEvent; import android.view.View; +import android.widget.LinearLayout; +import com.android.internal.util.LatencyTracker; +import com.android.internal.widget.LockPatternChecker; +import com.android.internal.widget.LockPatternUtils; import com.android.internal.widget.LockscreenCredential; +import com.android.systemui.Dependency; import com.android.systemui.R; /** * Base class for PIN and password unlock screens. */ -public abstract class KeyguardAbsKeyInputView extends KeyguardInputView { +public abstract class KeyguardAbsKeyInputView extends LinearLayout + implements KeyguardSecurityView, EmergencyButton.EmergencyButtonCallback { + protected KeyguardSecurityCallback mCallback; + protected LockPatternUtils mLockPatternUtils; + protected AsyncTask<?, ?, ?> mPendingLockCheck; + protected SecurityMessageDisplay mSecurityMessageDisplay; protected View mEcaView; protected boolean mEnableHaptics; + private boolean mDismissing; + protected boolean mResumed; + private CountDownTimer mCountdownTimer = null; + private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; // To avoid accidental lockout due to events while the device in in the pocket, ignore // any passwords with length less than or equal to this length. protected static final int MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT = 3; - private KeyDownListener mKeyDownListener; public KeyguardAbsKeyInputView(Context context) { this(context, null); @@ -43,10 +63,38 @@ public abstract class KeyguardAbsKeyInputView extends KeyguardInputView { public KeyguardAbsKeyInputView(Context context, AttributeSet attrs) { super(context, attrs); + mKeyguardUpdateMonitor = Dependency.get(KeyguardUpdateMonitor.class); } - void setEnableHaptics(boolean enableHaptics) { - mEnableHaptics = enableHaptics; + @Override + public void setKeyguardCallback(KeyguardSecurityCallback callback) { + mCallback = callback; + } + + @Override + public void setLockPatternUtils(LockPatternUtils utils) { + mLockPatternUtils = utils; + mEnableHaptics = mLockPatternUtils.isTactileFeedbackEnabled(); + } + + @Override + public void reset() { + // start fresh + mDismissing = false; + resetPasswordText(false /* animate */, false /* announce */); + // if the user is currently locked out, enforce it. + long deadline = mLockPatternUtils.getLockoutAttemptDeadline( + KeyguardUpdateMonitor.getCurrentUser()); + if (shouldLockout(deadline)) { + handleAttemptLockout(deadline); + } else { + resetState(); + } + } + + // Allow subclasses to override this behavior + protected boolean shouldLockout(long deadline) { + return deadline != 0; } protected abstract int getPasswordTextViewId(); @@ -54,7 +102,24 @@ public abstract class KeyguardAbsKeyInputView extends KeyguardInputView { @Override protected void onFinishInflate() { + mLockPatternUtils = new LockPatternUtils(mContext); mEcaView = findViewById(R.id.keyguard_selector_fade_container); + + EmergencyButton button = findViewById(R.id.emergency_call_button); + if (button != null) { + button.setCallback(this); + } + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + mSecurityMessageDisplay = KeyguardMessageArea.findSecurityMessageDisplay(this); + } + + @Override + public void onEmergencyButtonClickedWhenInCall() { + mCallback.reset(); } /* @@ -66,14 +131,195 @@ public abstract class KeyguardAbsKeyInputView extends KeyguardInputView { return R.string.kg_wrong_password; } + protected void verifyPasswordAndUnlock() { + if (mDismissing) return; // already verified but haven't been dismissed; don't do it again. + + final LockscreenCredential password = getEnteredCredential(); + setPasswordEntryInputEnabled(false); + if (mPendingLockCheck != null) { + mPendingLockCheck.cancel(false); + } + + final int userId = KeyguardUpdateMonitor.getCurrentUser(); + if (password.size() <= MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT) { + // to avoid accidental lockout, only count attempts that are long enough to be a + // real password. This may require some tweaking. + setPasswordEntryInputEnabled(true); + onPasswordChecked(userId, false /* matched */, 0, false /* not valid - too short */); + password.zeroize(); + return; + } + + if (LatencyTracker.isEnabled(mContext)) { + LatencyTracker.getInstance(mContext).onActionStart(ACTION_CHECK_CREDENTIAL); + LatencyTracker.getInstance(mContext).onActionStart(ACTION_CHECK_CREDENTIAL_UNLOCKED); + } + + mKeyguardUpdateMonitor.setCredentialAttempted(); + mPendingLockCheck = LockPatternChecker.checkCredential( + mLockPatternUtils, + password, + userId, + new LockPatternChecker.OnCheckCallback() { + + @Override + public void onEarlyMatched() { + if (LatencyTracker.isEnabled(mContext)) { + LatencyTracker.getInstance(mContext).onActionEnd( + ACTION_CHECK_CREDENTIAL); + } + onPasswordChecked(userId, true /* matched */, 0 /* timeoutMs */, + true /* isValidPassword */); + password.zeroize(); + } + + @Override + public void onChecked(boolean matched, int timeoutMs) { + if (LatencyTracker.isEnabled(mContext)) { + LatencyTracker.getInstance(mContext).onActionEnd( + ACTION_CHECK_CREDENTIAL_UNLOCKED); + } + setPasswordEntryInputEnabled(true); + mPendingLockCheck = null; + if (!matched) { + onPasswordChecked(userId, false /* matched */, timeoutMs, + true /* isValidPassword */); + } + password.zeroize(); + } + + @Override + public void onCancelled() { + // We already got dismissed with the early matched callback, so we cancelled + // the check. However, we still need to note down the latency. + if (LatencyTracker.isEnabled(mContext)) { + LatencyTracker.getInstance(mContext).onActionEnd( + ACTION_CHECK_CREDENTIAL_UNLOCKED); + } + password.zeroize(); + } + }); + } + + private void onPasswordChecked(int userId, boolean matched, int timeoutMs, + boolean isValidPassword) { + boolean dismissKeyguard = KeyguardUpdateMonitor.getCurrentUser() == userId; + if (matched) { + mCallback.reportUnlockAttempt(userId, true, 0); + if (dismissKeyguard) { + mDismissing = true; + mCallback.dismiss(true, userId); + } + } else { + if (isValidPassword) { + mCallback.reportUnlockAttempt(userId, false, timeoutMs); + if (timeoutMs > 0) { + long deadline = mLockPatternUtils.setLockoutAttemptDeadline( + userId, timeoutMs); + handleAttemptLockout(deadline); + } + } + if (timeoutMs == 0) { + mSecurityMessageDisplay.setMessage(getWrongPasswordStringId()); + } + } + resetPasswordText(true /* animate */, !matched /* announce deletion if no match */); + } + protected abstract void resetPasswordText(boolean animate, boolean announce); protected abstract LockscreenCredential getEnteredCredential(); protected abstract void setPasswordEntryEnabled(boolean enabled); protected abstract void setPasswordEntryInputEnabled(boolean enabled); + // Prevent user from using the PIN/Password entry until scheduled deadline. + protected void handleAttemptLockout(long elapsedRealtimeDeadline) { + setPasswordEntryEnabled(false); + long elapsedRealtime = SystemClock.elapsedRealtime(); + long secondsInFuture = (long) Math.ceil( + (elapsedRealtimeDeadline - elapsedRealtime) / 1000.0); + mCountdownTimer = new CountDownTimer(secondsInFuture * 1000, 1000) { + + @Override + public void onTick(long millisUntilFinished) { + int secondsRemaining = (int) Math.round(millisUntilFinished / 1000.0); + mSecurityMessageDisplay.setMessage(mContext.getResources().getQuantityString( + R.plurals.kg_too_many_failed_attempts_countdown, + secondsRemaining, secondsRemaining)); + } + + @Override + public void onFinish() { + mSecurityMessageDisplay.setMessage(""); + resetState(); + } + }.start(); + } + + protected void onUserInput() { + if (mCallback != null) { + mCallback.userActivity(); + mCallback.onUserInput(); + } + mSecurityMessageDisplay.setMessage(""); + } + @Override public boolean onKeyDown(int keyCode, KeyEvent event) { - return mKeyDownListener != null && mKeyDownListener.onKeyDown(keyCode, event); + // Fingerprint sensor sends a KeyEvent.KEYCODE_UNKNOWN. + // We don't want to consider it valid user input because the UI + // will already respond to the event. + if (keyCode != KeyEvent.KEYCODE_UNKNOWN) { + onUserInput(); + } + return false; + } + + @Override + public boolean needsInput() { + return false; + } + + @Override + public void onPause() { + mResumed = false; + + if (mCountdownTimer != null) { + mCountdownTimer.cancel(); + mCountdownTimer = null; + } + if (mPendingLockCheck != null) { + mPendingLockCheck.cancel(false); + mPendingLockCheck = null; + } + reset(); + } + + @Override + public void onResume(int reason) { + mResumed = true; + } + + @Override + public KeyguardSecurityCallback getCallback() { + return mCallback; + } + + @Override + public void showPromptReason(int reason) { + if (reason != PROMPT_REASON_NONE) { + int promtReasonStringRes = getPromptReasonStringRes(reason); + if (promtReasonStringRes != 0) { + mSecurityMessageDisplay.setMessage(promtReasonStringRes); + } + } + } + + @Override + public void showMessage(CharSequence message, ColorStateList colorState) { + if (colorState != null) { + mSecurityMessageDisplay.setNextMessageColor(colorState); + } + mSecurityMessageDisplay.setMessage(message); } protected abstract int getPromptReasonStringRes(int reason); @@ -87,12 +333,9 @@ public abstract class KeyguardAbsKeyInputView extends KeyguardInputView { } } - public void setKeyDownListener(KeyDownListener keyDownListener) { - mKeyDownListener = keyDownListener; - } - - public interface KeyDownListener { - boolean onKeyDown(int keyCode, KeyEvent keyEvent); + @Override + public boolean startDisappearAnimation(Runnable finishRunnable) { + return false; } } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java deleted file mode 100644 index d957628c52ab..000000000000 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java +++ /dev/null @@ -1,275 +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.keyguard; - -import static com.android.internal.util.LatencyTracker.ACTION_CHECK_CREDENTIAL; -import static com.android.internal.util.LatencyTracker.ACTION_CHECK_CREDENTIAL_UNLOCKED; -import static com.android.keyguard.KeyguardAbsKeyInputView.MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT; - -import android.content.res.ColorStateList; -import android.os.AsyncTask; -import android.os.CountDownTimer; -import android.os.SystemClock; -import android.view.KeyEvent; - -import com.android.internal.util.LatencyTracker; -import com.android.internal.widget.LockPatternChecker; -import com.android.internal.widget.LockPatternUtils; -import com.android.internal.widget.LockscreenCredential; -import com.android.keyguard.EmergencyButton.EmergencyButtonCallback; -import com.android.keyguard.KeyguardAbsKeyInputView.KeyDownListener; -import com.android.keyguard.KeyguardSecurityModel.SecurityMode; -import com.android.systemui.R; - -public abstract class KeyguardAbsKeyInputViewController<T extends KeyguardAbsKeyInputView> - extends KeyguardInputViewController<T> { - private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; - private final LockPatternUtils mLockPatternUtils; - private final LatencyTracker mLatencyTracker; - private CountDownTimer mCountdownTimer; - protected KeyguardMessageAreaController mMessageAreaController; - private boolean mDismissing; - protected AsyncTask<?, ?, ?> mPendingLockCheck; - protected boolean mResumed; - - private final KeyDownListener mKeyDownListener = (keyCode, keyEvent) -> { - // Fingerprint sensor sends a KeyEvent.KEYCODE_UNKNOWN. - // We don't want to consider it valid user input because the UI - // will already respond to the event. - if (keyCode != KeyEvent.KEYCODE_UNKNOWN) { - onUserInput(); - } - return false; - }; - - private final EmergencyButtonCallback mEmergencyButtonCallback = new EmergencyButtonCallback() { - @Override - public void onEmergencyButtonClickedWhenInCall() { - getKeyguardSecurityCallback().reset(); - } - }; - - protected KeyguardAbsKeyInputViewController(T view, - KeyguardUpdateMonitor keyguardUpdateMonitor, - SecurityMode securityMode, - LockPatternUtils lockPatternUtils, - KeyguardSecurityCallback keyguardSecurityCallback, - KeyguardMessageAreaController.Factory messageAreaControllerFactory, - LatencyTracker latencyTracker) { - super(view, securityMode, keyguardSecurityCallback); - mKeyguardUpdateMonitor = keyguardUpdateMonitor; - mLockPatternUtils = lockPatternUtils; - mLatencyTracker = latencyTracker; - KeyguardMessageArea kma = KeyguardMessageArea.findSecurityMessageDisplay(mView); - mMessageAreaController = messageAreaControllerFactory.create(kma); - } - - abstract void resetState(); - - @Override - public void init() { - super.init(); - mMessageAreaController.init(); - } - - @Override - protected void onViewAttached() { - mView.setKeyDownListener(mKeyDownListener); - mView.setEnableHaptics(mLockPatternUtils.isTactileFeedbackEnabled()); - EmergencyButton button = mView.findViewById(R.id.emergency_call_button); - if (button != null) { - button.setCallback(mEmergencyButtonCallback); - } - } - - @Override - public void reset() { - // start fresh - mDismissing = false; - mView.resetPasswordText(false /* animate */, false /* announce */); - // if the user is currently locked out, enforce it. - long deadline = mLockPatternUtils.getLockoutAttemptDeadline( - KeyguardUpdateMonitor.getCurrentUser()); - if (shouldLockout(deadline)) { - handleAttemptLockout(deadline); - } else { - mView.resetState(); - } - } - - @Override - public boolean needsInput() { - return false; - } - - @Override - public void showMessage(CharSequence message, ColorStateList colorState) { - if (colorState != null) { - mMessageAreaController.setNextMessageColor(colorState); - } - mMessageAreaController.setMessage(message); - } - - // Allow subclasses to override this behavior - protected boolean shouldLockout(long deadline) { - return deadline != 0; - } - - // Prevent user from using the PIN/Password entry until scheduled deadline. - protected void handleAttemptLockout(long elapsedRealtimeDeadline) { - mView.setPasswordEntryEnabled(false); - long elapsedRealtime = SystemClock.elapsedRealtime(); - long secondsInFuture = (long) Math.ceil( - (elapsedRealtimeDeadline - elapsedRealtime) / 1000.0); - mCountdownTimer = new CountDownTimer(secondsInFuture * 1000, 1000) { - - @Override - public void onTick(long millisUntilFinished) { - int secondsRemaining = (int) Math.round(millisUntilFinished / 1000.0); - mMessageAreaController.setMessage(mView.getResources().getQuantityString( - R.plurals.kg_too_many_failed_attempts_countdown, - secondsRemaining, secondsRemaining)); - } - - @Override - public void onFinish() { - mMessageAreaController.setMessage(""); - resetState(); - } - }.start(); - } - - void onPasswordChecked(int userId, boolean matched, int timeoutMs, boolean isValidPassword) { - boolean dismissKeyguard = KeyguardUpdateMonitor.getCurrentUser() == userId; - if (matched) { - getKeyguardSecurityCallback().reportUnlockAttempt(userId, true, 0); - if (dismissKeyguard) { - mDismissing = true; - getKeyguardSecurityCallback().dismiss(true, userId); - } - } else { - if (isValidPassword) { - getKeyguardSecurityCallback().reportUnlockAttempt(userId, false, timeoutMs); - if (timeoutMs > 0) { - long deadline = mLockPatternUtils.setLockoutAttemptDeadline( - userId, timeoutMs); - handleAttemptLockout(deadline); - } - } - if (timeoutMs == 0) { - mMessageAreaController.setMessage(mView.getWrongPasswordStringId()); - } - } - mView.resetPasswordText(true /* animate */, !matched /* announce deletion if no match */); - } - - protected void verifyPasswordAndUnlock() { - if (mDismissing) return; // already verified but haven't been dismissed; don't do it again. - - final LockscreenCredential password = mView.getEnteredCredential(); - mView.setPasswordEntryInputEnabled(false); - if (mPendingLockCheck != null) { - mPendingLockCheck.cancel(false); - } - - final int userId = KeyguardUpdateMonitor.getCurrentUser(); - if (password.size() <= MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT) { - // to avoid accidental lockout, only count attempts that are long enough to be a - // real password. This may require some tweaking. - mView.setPasswordEntryInputEnabled(true); - onPasswordChecked(userId, false /* matched */, 0, false /* not valid - too short */); - password.zeroize(); - return; - } - - mLatencyTracker.onActionStart(ACTION_CHECK_CREDENTIAL); - mLatencyTracker.onActionStart(ACTION_CHECK_CREDENTIAL_UNLOCKED); - - mKeyguardUpdateMonitor.setCredentialAttempted(); - mPendingLockCheck = LockPatternChecker.checkCredential( - mLockPatternUtils, - password, - userId, - new LockPatternChecker.OnCheckCallback() { - - @Override - public void onEarlyMatched() { - mLatencyTracker.onActionEnd(ACTION_CHECK_CREDENTIAL); - - onPasswordChecked(userId, true /* matched */, 0 /* timeoutMs */, - true /* isValidPassword */); - password.zeroize(); - } - - @Override - public void onChecked(boolean matched, int timeoutMs) { - mLatencyTracker.onActionEnd(ACTION_CHECK_CREDENTIAL_UNLOCKED); - mView.setPasswordEntryInputEnabled(true); - mPendingLockCheck = null; - if (!matched) { - onPasswordChecked(userId, false /* matched */, timeoutMs, - true /* isValidPassword */); - } - password.zeroize(); - } - - @Override - public void onCancelled() { - // We already got dismissed with the early matched callback, so we cancelled - // the check. However, we still need to note down the latency. - mLatencyTracker.onActionEnd(ACTION_CHECK_CREDENTIAL_UNLOCKED); - password.zeroize(); - } - }); - } - - @Override - public void showPromptReason(int reason) { - if (reason != PROMPT_REASON_NONE) { - int promtReasonStringRes = mView.getPromptReasonStringRes(reason); - if (promtReasonStringRes != 0) { - mMessageAreaController.setMessage(promtReasonStringRes); - } - } - } - - protected void onUserInput() { - getKeyguardSecurityCallback().userActivity(); - getKeyguardSecurityCallback().onUserInput(); - mMessageAreaController.setMessage(""); - } - - @Override - public void onResume(int reason) { - mResumed = true; - } - - @Override - public void onPause() { - mResumed = false; - - if (mCountdownTimer != null) { - mCountdownTimer.cancel(); - mCountdownTimer = null; - } - if (mPendingLockCheck != null) { - mPendingLockCheck.cancel(false); - mPendingLockCheck = null; - } - reset(); - } -} diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java index 36d5543f1c01..be21d203411e 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java @@ -39,6 +39,7 @@ import com.android.systemui.Dependency; import com.android.systemui.R; import com.android.systemui.navigationbar.NavigationBarController; import com.android.systemui.navigationbar.NavigationBarView; +import com.android.systemui.util.InjectionInflationController; import javax.inject.Inject; @@ -48,6 +49,7 @@ public class KeyguardDisplayManager { private final MediaRouter mMediaRouter; private final DisplayManager mDisplayService; + private final InjectionInflationController mInjectableInflater; private final KeyguardStatusViewComponent.Factory mKeyguardStatusViewComponentFactory; private final Context mContext; @@ -90,8 +92,10 @@ public class KeyguardDisplayManager { @Inject public KeyguardDisplayManager(Context context, + InjectionInflationController injectableInflater, KeyguardStatusViewComponent.Factory keyguardStatusViewComponentFactory) { mContext = context; + mInjectableInflater = injectableInflater; mKeyguardStatusViewComponentFactory = keyguardStatusViewComponentFactory; mMediaRouter = mContext.getSystemService(MediaRouter.class); mDisplayService = mContext.getSystemService(DisplayManager.class); @@ -127,7 +131,8 @@ public class KeyguardDisplayManager { Presentation presentation = mPresentations.get(displayId); if (presentation == null) { final Presentation newPresentation = new KeyguardPresentation(mContext, display, - mKeyguardStatusViewComponentFactory, LayoutInflater.from(mContext)); + mKeyguardStatusViewComponentFactory, + mInjectableInflater.injectable(LayoutInflater.from(mContext))); newPresentation.setOnDismissListener(dialog -> { if (newPresentation.equals(mPresentations.get(displayId))) { mPresentations.remove(displayId); @@ -245,7 +250,7 @@ public class KeyguardDisplayManager { private static final int VIDEO_SAFE_REGION = 80; // Percentage of display width & height private static final int MOVE_CLOCK_TIMEOUT = 10000; // 10s private final KeyguardStatusViewComponent.Factory mKeyguardStatusViewComponentFactory; - private final LayoutInflater mLayoutInflater; + private final LayoutInflater mInjectableLayoutInflater; private KeyguardClockSwitchController mKeyguardClockSwitchController; private View mClock; private int mUsableWidth; @@ -265,10 +270,10 @@ public class KeyguardDisplayManager { KeyguardPresentation(Context context, Display display, KeyguardStatusViewComponent.Factory keyguardStatusViewComponentFactory, - LayoutInflater layoutInflater) { + LayoutInflater injectionLayoutInflater) { super(context, display, R.style.Theme_SystemUI_KeyguardPresentation); mKeyguardStatusViewComponentFactory = keyguardStatusViewComponentFactory; - mLayoutInflater = layoutInflater; + mInjectableLayoutInflater = injectionLayoutInflater; getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); setCancelable(false); } @@ -294,7 +299,7 @@ public class KeyguardDisplayManager { mMarginLeft = (100 - VIDEO_SAFE_REGION) * p.x / 200; mMarginTop = (100 - VIDEO_SAFE_REGION) * p.y / 200; - setContentView(mLayoutInflater.inflate(R.layout.keyguard_presentation, null)); + setContentView(mInjectableLayoutInflater.inflate(R.layout.keyguard_presentation, null)); // Logic to make the lock screen fullscreen getWindow().getDecorView().setSystemUiVisibility( diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java index 9ffa658da0e8..7aabb17de90c 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java @@ -178,18 +178,18 @@ public class KeyguardHostViewController extends ViewController<KeyguardHostView> /** Initialize the Controller. */ public void init() { super.init(); + mView.setViewMediatorCallback(mViewMediatorCallback); + // Update ViewMediator with the current input method requirements + mViewMediatorCallback.setNeedsInput(mKeyguardSecurityContainerController.needsInput()); mKeyguardSecurityContainerController.init(); + mKeyguardSecurityContainerController.setSecurityCallback(mSecurityCallback); + mKeyguardSecurityContainerController.showPrimarySecurityScreen(false); } @Override protected void onViewAttached() { - mView.setViewMediatorCallback(mViewMediatorCallback); - // Update ViewMediator with the current input method requirements - mViewMediatorCallback.setNeedsInput(mKeyguardSecurityContainerController.needsInput()); mKeyguardUpdateMonitor.registerCallback(mUpdateCallback); mView.setOnKeyListener(mOnKeyListener); - mKeyguardSecurityContainerController.setSecurityCallback(mSecurityCallback); - mKeyguardSecurityContainerController.showPrimarySecurityScreen(false); } @Override @@ -350,7 +350,7 @@ public class KeyguardHostViewController extends ViewController<KeyguardHostView> } public boolean handleBackKey() { - if (mKeyguardSecurityContainerController.getCurrentSecurityMode() + if (mKeyguardSecurityContainerController.getCurrentSecuritySelection() != SecurityMode.None) { mKeyguardSecurityContainerController.dismiss( false, KeyguardUpdateMonitor.getCurrentUser()); diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardInputView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardInputView.java deleted file mode 100644 index d42a53cc875e..000000000000 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardInputView.java +++ /dev/null @@ -1,55 +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.keyguard; - -import android.content.Context; -import android.util.AttributeSet; -import android.view.MotionEvent; -import android.widget.LinearLayout; - -import androidx.annotation.Nullable; - -/** - * A Base class for all Keyguard password/pattern/pin related inputs. - */ -public abstract class KeyguardInputView extends LinearLayout { - - public KeyguardInputView(Context context) { - super(context); - } - - public KeyguardInputView(Context context, - @Nullable AttributeSet attrs) { - super(context, attrs); - } - - public KeyguardInputView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - } - - abstract CharSequence getTitle(); - - boolean disallowInterceptTouch(MotionEvent event) { - return false; - } - - void startAppearAnimation() {} - - boolean startDisappearAnimation(Runnable finishRunnable) { - return false; - } -} diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java deleted file mode 100644 index fbda818740e8..000000000000 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java +++ /dev/null @@ -1,196 +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.keyguard; - -import android.content.res.ColorStateList; -import android.content.res.Resources; -import android.telephony.TelephonyManager; -import android.view.inputmethod.InputMethodManager; - -import com.android.internal.util.LatencyTracker; -import com.android.internal.widget.LockPatternUtils; -import com.android.keyguard.KeyguardSecurityModel.SecurityMode; -import com.android.systemui.dagger.qualifiers.Main; -import com.android.systemui.util.ViewController; -import com.android.systemui.util.concurrency.DelayableExecutor; - -import javax.inject.Inject; - - -/** Controller for a {@link KeyguardSecurityView}. */ -public abstract class KeyguardInputViewController<T extends KeyguardInputView> - extends ViewController<T> implements KeyguardSecurityView { - - private final SecurityMode mSecurityMode; - private final KeyguardSecurityCallback mKeyguardSecurityCallback; - private boolean mPaused; - - - // The following is used to ignore callbacks from SecurityViews that are no longer current - // (e.g. face unlock). This avoids unwanted asynchronous events from messing with the - // state for the current security method. - private KeyguardSecurityCallback mNullCallback = new KeyguardSecurityCallback() { - @Override - public void userActivity() { } - @Override - public void reportUnlockAttempt(int userId, boolean success, int timeoutMs) { } - @Override - public boolean isVerifyUnlockOnly() { - return false; - } - @Override - public void dismiss(boolean securityVerified, int targetUserId) { } - @Override - public void dismiss(boolean authenticated, int targetId, - boolean bypassSecondaryLockScreen) { } - @Override - public void onUserInput() { } - @Override - public void reset() {} - }; - - protected KeyguardInputViewController(T view, SecurityMode securityMode, - KeyguardSecurityCallback keyguardSecurityCallback) { - super(view); - mSecurityMode = securityMode; - mKeyguardSecurityCallback = keyguardSecurityCallback; - } - - @Override - protected void onViewAttached() { - } - - @Override - protected void onViewDetached() { - } - - SecurityMode getSecurityMode() { - return mSecurityMode; - } - - protected KeyguardSecurityCallback getKeyguardSecurityCallback() { - if (mPaused) { - return mNullCallback; - } - - return mKeyguardSecurityCallback; - } - - @Override - public void reset() { - } - - @Override - public void onPause() { - mPaused = true; - } - - @Override - public void onResume(int reason) { - mPaused = false; - } - - @Override - public void showPromptReason(int reason) { - } - - @Override - public void showMessage(CharSequence message, ColorStateList colorState) { - } - - public void startAppearAnimation() { - mView.startAppearAnimation(); - } - - public boolean startDisappearAnimation(Runnable finishRunnable) { - return mView.startDisappearAnimation(finishRunnable); - } - - @Override - public CharSequence getTitle() { - return mView.getTitle(); - } - - /** Finds the index of this view in the suppplied parent view. */ - public int getIndexIn(KeyguardSecurityViewFlipper view) { - return view.indexOfChild(mView); - } - - /** Factory for a {@link KeyguardInputViewController}. */ - public static class Factory { - private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; - private final LockPatternUtils mLockPatternUtils; - private final LatencyTracker mLatencyTracker; - private final KeyguardMessageAreaController.Factory mMessageAreaControllerFactory; - private final InputMethodManager mInputMethodManager; - private final DelayableExecutor mMainExecutor; - private final Resources mResources; - private LiftToActivateListener mLiftToActivateListener; - private TelephonyManager mTelephonyManager; - - @Inject - public Factory(KeyguardUpdateMonitor keyguardUpdateMonitor, - LockPatternUtils lockPatternUtils, - LatencyTracker latencyTracker, - KeyguardMessageAreaController.Factory messageAreaControllerFactory, - InputMethodManager inputMethodManager, @Main DelayableExecutor mainExecutor, - @Main Resources resources, LiftToActivateListener liftToActivateListener, - TelephonyManager telephonyManager) { - mKeyguardUpdateMonitor = keyguardUpdateMonitor; - mLockPatternUtils = lockPatternUtils; - mLatencyTracker = latencyTracker; - mMessageAreaControllerFactory = messageAreaControllerFactory; - mInputMethodManager = inputMethodManager; - mMainExecutor = mainExecutor; - mResources = resources; - mLiftToActivateListener = liftToActivateListener; - mTelephonyManager = telephonyManager; - } - - /** Create a new {@link KeyguardInputViewController}. */ - public KeyguardInputViewController create(KeyguardInputView keyguardInputView, - SecurityMode securityMode, KeyguardSecurityCallback keyguardSecurityCallback) { - if (keyguardInputView instanceof KeyguardPatternView) { - return new KeyguardPatternViewController((KeyguardPatternView) keyguardInputView, - mKeyguardUpdateMonitor, securityMode, mLockPatternUtils, - keyguardSecurityCallback, mLatencyTracker, mMessageAreaControllerFactory); - } else if (keyguardInputView instanceof KeyguardPasswordView) { - return new KeyguardPasswordViewController((KeyguardPasswordView) keyguardInputView, - mKeyguardUpdateMonitor, securityMode, mLockPatternUtils, - keyguardSecurityCallback, mMessageAreaControllerFactory, mLatencyTracker, - mInputMethodManager, mMainExecutor, mResources); - } else if (keyguardInputView instanceof KeyguardPINView) { - return new KeyguardPinViewController((KeyguardPINView) keyguardInputView, - mKeyguardUpdateMonitor, securityMode, mLockPatternUtils, - keyguardSecurityCallback, mMessageAreaControllerFactory, mLatencyTracker, - mLiftToActivateListener); - } else if (keyguardInputView instanceof KeyguardSimPinView) { - return new KeyguardSimPinViewController((KeyguardSimPinView) keyguardInputView, - mKeyguardUpdateMonitor, securityMode, mLockPatternUtils, - keyguardSecurityCallback, mMessageAreaControllerFactory, mLatencyTracker, - mLiftToActivateListener, mTelephonyManager); - } else if (keyguardInputView instanceof KeyguardSimPukView) { - return new KeyguardSimPukViewController((KeyguardSimPukView) keyguardInputView, - mKeyguardUpdateMonitor, securityMode, mLockPatternUtils, - keyguardSecurityCallback, mMessageAreaControllerFactory, mLatencyTracker, - mLiftToActivateListener, mTelephonyManager); - } - - throw new RuntimeException("Unable to find controller for " + keyguardInputView); - } - } -} diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageArea.java b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageArea.java index 1a0a4370fca4..a8b1451d92c7 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageArea.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageArea.java @@ -16,6 +16,8 @@ package com.android.keyguard; +import static com.android.systemui.util.InjectionInflationController.VIEW_CONTEXT; + import android.content.Context; import android.content.res.ColorStateList; import android.content.res.TypedArray; @@ -29,14 +31,20 @@ import android.util.TypedValue; import android.view.View; import android.widget.TextView; +import com.android.systemui.Dependency; import com.android.systemui.R; +import com.android.systemui.statusbar.policy.ConfigurationController; import java.lang.ref.WeakReference; +import javax.inject.Inject; +import javax.inject.Named; + /*** * Manages a number of views inside of the given layout. See below for a list of widgets. */ -public class KeyguardMessageArea extends TextView implements SecurityMessageDisplay { +public class KeyguardMessageArea extends TextView implements SecurityMessageDisplay, + ConfigurationController.ConfigurationListener { /** Handler token posted with accessibility announcement runnables. */ private static final Object ANNOUNCE_TOKEN = new Object(); @@ -48,26 +56,71 @@ public class KeyguardMessageArea extends TextView implements SecurityMessageDisp private static final int DEFAULT_COLOR = -1; private final Handler mHandler; + private final ConfigurationController mConfigurationController; private ColorStateList mDefaultColorState; private CharSequence mMessage; private ColorStateList mNextMessageColorState = ColorStateList.valueOf(DEFAULT_COLOR); private boolean mBouncerVisible; - public KeyguardMessageArea(Context context, AttributeSet attrs) { + private KeyguardUpdateMonitorCallback mInfoCallback = new KeyguardUpdateMonitorCallback() { + public void onFinishedGoingToSleep(int why) { + setSelected(false); + } + + public void onStartedWakingUp() { + setSelected(true); + } + + @Override + public void onKeyguardBouncerChanged(boolean bouncer) { + mBouncerVisible = bouncer; + update(); + } + }; + + public KeyguardMessageArea(Context context) { + super(context, null); + throw new IllegalStateException("This constructor should never be invoked"); + } + + @Inject + public KeyguardMessageArea(@Named(VIEW_CONTEXT) Context context, AttributeSet attrs, + ConfigurationController configurationController) { + this(context, attrs, Dependency.get(KeyguardUpdateMonitor.class), configurationController); + } + + public KeyguardMessageArea(Context context, AttributeSet attrs, KeyguardUpdateMonitor monitor, + ConfigurationController configurationController) { super(context, attrs); setLayerType(LAYER_TYPE_HARDWARE, null); // work around nested unclipped SaveLayer bug + monitor.registerCallback(mInfoCallback); mHandler = new Handler(Looper.myLooper()); + mConfigurationController = configurationController; onThemeChanged(); } @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + mConfigurationController.addCallback(this); + onThemeChanged(); + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + mConfigurationController.removeCallback(this); + } + + @Override public void setNextMessageColor(ColorStateList colorState) { mNextMessageColorState = colorState; } - void onThemeChanged() { + @Override + public void onThemeChanged() { TypedArray array = mContext.obtainStyledAttributes(new int[] { R.attr.wallpaperTextColor }); @@ -77,7 +130,8 @@ public class KeyguardMessageArea extends TextView implements SecurityMessageDisp update(); } - void onDensityOrFontScaleChanged() { + @Override + public void onDensityOrFontScaleChanged() { TypedArray array = mContext.obtainStyledAttributes(R.style.Keyguard_TextView, new int[] { android.R.attr.textSize }); @@ -123,6 +177,12 @@ public class KeyguardMessageArea extends TextView implements SecurityMessageDisp return messageArea; } + @Override + protected void onFinishInflate() { + boolean shouldMarquee = Dependency.get(KeyguardUpdateMonitor.class).isDeviceInteractive(); + setSelected(shouldMarquee); // This is required to ensure marquee works + } + private void securityMessageChanged(CharSequence message) { mMessage = message; update(); @@ -136,7 +196,7 @@ public class KeyguardMessageArea extends TextView implements SecurityMessageDisp update(); } - void update() { + private void update() { CharSequence status = mMessage; setVisibility(TextUtils.isEmpty(status) || !mBouncerVisible ? INVISIBLE : VISIBLE); setText(status); @@ -148,9 +208,6 @@ public class KeyguardMessageArea extends TextView implements SecurityMessageDisp setTextColor(colorState); } - public void setBouncerVisible(boolean bouncerVisible) { - mBouncerVisible = bouncerVisible; - } /** * Runnable used to delay accessibility announcements. diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java index 1618e8e58055..f056bdbb9706 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java @@ -16,10 +16,7 @@ package com.android.keyguard; -import android.content.res.ColorStateList; - import com.android.systemui.statusbar.policy.ConfigurationController; -import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener; import com.android.systemui.util.ViewController; import javax.inject.Inject; @@ -29,35 +26,6 @@ public class KeyguardMessageAreaController extends ViewController<KeyguardMessag private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; private final ConfigurationController mConfigurationController; - - private KeyguardUpdateMonitorCallback mInfoCallback = new KeyguardUpdateMonitorCallback() { - public void onFinishedGoingToSleep(int why) { - mView.setSelected(false); - } - - public void onStartedWakingUp() { - mView.setSelected(true); - } - - @Override - public void onKeyguardBouncerChanged(boolean bouncer) { - mView.setBouncerVisible(bouncer); - mView.update(); - } - }; - - private ConfigurationListener mConfigurationListener = new ConfigurationListener() { - @Override - public void onThemeChanged() { - mView.onThemeChanged(); - } - - @Override - public void onDensityOrFontScaleChanged() { - mView.onDensityOrFontScaleChanged(); - } - }; - private KeyguardMessageAreaController(KeyguardMessageArea view, KeyguardUpdateMonitor keyguardUpdateMonitor, ConfigurationController configurationController) { @@ -69,31 +37,17 @@ public class KeyguardMessageAreaController extends ViewController<KeyguardMessag @Override protected void onViewAttached() { - mConfigurationController.addCallback(mConfigurationListener); - mKeyguardUpdateMonitor.registerCallback(mInfoCallback); - mView.setSelected(mKeyguardUpdateMonitor.isDeviceInteractive()); - mView.onThemeChanged(); + //mConfigurationController.addCallback(); + //mKeyguardUpdateMonitor.registerCallback(); } @Override protected void onViewDetached() { - mConfigurationController.removeCallback(mConfigurationListener); - mKeyguardUpdateMonitor.removeCallback(mInfoCallback); - } - - public void setMessage(CharSequence s) { - mView.setMessage(s); - } - - public void setMessage(int resId) { - mView.setMessage(resId); - } - - public void setNextMessageColor(ColorStateList colorState) { - mView.setNextMessageColor(colorState); + //mConfigurationController.removeCallback(); + //mKeyguardUpdateMonitor.removeCallback(); } - /** Factory for creating {@link com.android.keyguard.KeyguardMessageAreaController}. */ + /** Factory for createing {@link com.android.keyguard.KeyguardMessageAreaController}. */ public static class Factory { private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; private final ConfigurationController mConfigurationController; diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java index 580d7043a220..12ea1d586e10 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java @@ -24,6 +24,7 @@ import android.view.animation.AnimationUtils; import com.android.settingslib.animation.AppearAnimationUtils; import com.android.settingslib.animation.DisappearAnimationUtils; +import com.android.systemui.Dependency; import com.android.systemui.R; /** @@ -39,8 +40,10 @@ public class KeyguardPINView extends KeyguardPinBasedInputView { private ViewGroup mRow1; private ViewGroup mRow2; private ViewGroup mRow3; + private View mDivider; private int mDisappearYTranslation; private View[][] mViews; + private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; public KeyguardPINView(Context context) { this(context, null); @@ -60,10 +63,15 @@ public class KeyguardPINView extends KeyguardPinBasedInputView { mContext, android.R.interpolator.fast_out_linear_in)); mDisappearYTranslation = getResources().getDimensionPixelSize( R.dimen.disappear_y_translation); + mKeyguardUpdateMonitor = Dependency.get(KeyguardUpdateMonitor.class); } @Override protected void resetState() { + super.resetState(); + if (mSecurityMessageDisplay != null) { + mSecurityMessageDisplay.setMessage(""); + } } @Override @@ -80,6 +88,7 @@ public class KeyguardPINView extends KeyguardPinBasedInputView { mRow1 = findViewById(R.id.row1); mRow2 = findViewById(R.id.row2); mRow3 = findViewById(R.id.row3); + mDivider = findViewById(R.id.divider); mViews = new View[][]{ new View[]{ mRow0, null, null @@ -103,6 +112,18 @@ public class KeyguardPINView extends KeyguardPinBasedInputView { new View[]{ null, mEcaView, null }}; + + View cancelBtn = findViewById(R.id.cancel_button); + if (cancelBtn != null) { + cancelBtn.setOnClickListener(view -> { + mCallback.reset(); + mCallback.onCancelClicked(); + }); + } + } + + @Override + public void showUsabilityHint() { } @Override @@ -126,21 +147,24 @@ public class KeyguardPINView extends KeyguardPinBasedInputView { }); } - public boolean startDisappearAnimation(boolean needsSlowUnlockTransition, - final Runnable finishRunnable) { - + @Override + public boolean startDisappearAnimation(final Runnable finishRunnable) { enableClipping(false); setTranslationY(0); AppearAnimationUtils.startTranslationYAnimation(this, 0 /* delay */, 280 /* duration */, mDisappearYTranslation, mDisappearAnimationUtils.getInterpolator()); - DisappearAnimationUtils disappearAnimationUtils = needsSlowUnlockTransition + DisappearAnimationUtils disappearAnimationUtils = mKeyguardUpdateMonitor + .needsSlowUnlockTransition() ? mDisappearAnimationUtilsLocked : mDisappearAnimationUtils; disappearAnimationUtils.startAnimation2d(mViews, - () -> { - enableClipping(true); - if (finishRunnable != null) { - finishRunnable.run(); + new Runnable() { + @Override + public void run() { + enableClipping(true); + if (finishRunnable != null) { + finishRunnable.run(); + } } }); return true; diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java index aaa5efec807e..97317cf5580f 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java @@ -16,37 +16,50 @@ package com.android.keyguard; -import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_DEVICE_ADMIN; -import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_NONE; -import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_PREPARE_FOR_UPDATE; -import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_RESTART; -import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_TIMEOUT; -import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_USER_REQUEST; - import android.content.Context; import android.graphics.Rect; +import android.os.UserHandle; +import android.text.Editable; +import android.text.InputType; +import android.text.TextUtils; +import android.text.TextWatcher; +import android.text.method.TextKeyListener; import android.util.AttributeSet; +import android.view.KeyEvent; +import android.view.View; import android.view.animation.AnimationUtils; import android.view.animation.Interpolator; +import android.view.inputmethod.EditorInfo; +import android.view.inputmethod.InputMethodInfo; +import android.view.inputmethod.InputMethodManager; +import android.view.inputmethod.InputMethodSubtype; import android.widget.TextView; +import android.widget.TextView.OnEditorActionListener; import com.android.internal.widget.LockscreenCredential; import com.android.internal.widget.TextViewInputDisabler; import com.android.systemui.R; + +import java.util.List; /** * Displays an alphanumeric (latin-1) key entry for the user to enter * an unlock password */ -public class KeyguardPasswordView extends KeyguardAbsKeyInputView { +public class KeyguardPasswordView extends KeyguardAbsKeyInputView + implements KeyguardSecurityView, OnEditorActionListener, TextWatcher { + private final boolean mShowImeAtScreenOn; private final int mDisappearYTranslation; // A delay constant to be used in a workaround for the situation where InputMethodManagerService // is not switched to the new user yet. // TODO: Remove this by ensuring such a race condition never happens. + private static final int DELAY_MILLIS_TO_REEVALUATE_IME_SWITCH_ICON = 500; // 500ms + InputMethodManager mImm; private TextView mPasswordEntry; private TextViewInputDisabler mPasswordEntryDisabler; + private View mSwitchImeButton; private Interpolator mLinearOutSlowInInterpolator; private Interpolator mFastOutLinearInInterpolator; @@ -57,6 +70,8 @@ public class KeyguardPasswordView extends KeyguardAbsKeyInputView { public KeyguardPasswordView(Context context, AttributeSet attrs) { super(context, attrs); + mShowImeAtScreenOn = context.getResources(). + getBoolean(R.bool.kg_show_ime_at_screen_on); mDisappearYTranslation = getResources().getDimensionPixelSize( R.dimen.disappear_y_translation); mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator( @@ -67,6 +82,20 @@ public class KeyguardPasswordView extends KeyguardAbsKeyInputView { @Override protected void resetState() { + mPasswordEntry.setTextOperationUser(UserHandle.of(KeyguardUpdateMonitor.getCurrentUser())); + if (mSecurityMessageDisplay != null) { + mSecurityMessageDisplay.setMessage(""); + } + final boolean wasDisabled = mPasswordEntry.isEnabled(); + setPasswordEntryEnabled(true); + setPasswordEntryInputEnabled(true); + // Don't call showSoftInput when PasswordEntry is invisible or in pausing stage. + if (!mResumed || !mPasswordEntry.isVisibleToUser()) { + return; + } + if (wasDisabled) { + mImm.showSoftInput(mPasswordEntry, InputMethodManager.SHOW_IMPLICIT); + } } @Override @@ -75,6 +104,29 @@ public class KeyguardPasswordView extends KeyguardAbsKeyInputView { } @Override + public boolean needsInput() { + return true; + } + + @Override + public void onResume(final int reason) { + super.onResume(reason); + + // Wait a bit to focus the field so the focusable flag on the window is already set then. + post(new Runnable() { + @Override + public void run() { + if (isShown() && mPasswordEntry.isEnabled()) { + mPasswordEntry.requestFocus(); + if (reason != KeyguardSecurityView.SCREEN_ON || mShowImeAtScreenOn) { + mImm.showSoftInput(mPasswordEntry, InputMethodManager.SHOW_IMPLICIT); + } + } + } + }); + } + + @Override protected int getPromptReasonStringRes(int reason) { switch (reason) { case PROMPT_REASON_RESTART: @@ -94,13 +146,97 @@ public class KeyguardPasswordView extends KeyguardAbsKeyInputView { } } + @Override + public void onPause() { + super.onPause(); + mImm.hideSoftInputFromWindow(getWindowToken(), 0); + } + + @Override + public void onStartingToHide() { + mImm.hideSoftInputFromWindow(getWindowToken(), 0); + } + + private void updateSwitchImeButton() { + // If there's more than one IME, enable the IME switcher button + final boolean wasVisible = mSwitchImeButton.getVisibility() == View.VISIBLE; + final boolean shouldBeVisible = hasMultipleEnabledIMEsOrSubtypes(mImm, false); + if (wasVisible != shouldBeVisible) { + mSwitchImeButton.setVisibility(shouldBeVisible ? View.VISIBLE : View.GONE); + } + + // TODO: Check if we still need this hack. + // If no icon is visible, reset the start margin on the password field so the text is + // still centered. + if (mSwitchImeButton.getVisibility() != View.VISIBLE) { + android.view.ViewGroup.LayoutParams params = mPasswordEntry.getLayoutParams(); + if (params instanceof MarginLayoutParams) { + final MarginLayoutParams mlp = (MarginLayoutParams) params; + mlp.setMarginStart(0); + mPasswordEntry.setLayoutParams(params); + } + } + } @Override protected void onFinishInflate() { super.onFinishInflate(); + mImm = (InputMethodManager) getContext().getSystemService( + Context.INPUT_METHOD_SERVICE); + mPasswordEntry = findViewById(getPasswordTextViewId()); + mPasswordEntry.setTextOperationUser(UserHandle.of(KeyguardUpdateMonitor.getCurrentUser())); mPasswordEntryDisabler = new TextViewInputDisabler(mPasswordEntry); + mPasswordEntry.setKeyListener(TextKeyListener.getInstance()); + mPasswordEntry.setInputType(InputType.TYPE_CLASS_TEXT + | InputType.TYPE_TEXT_VARIATION_PASSWORD); + mPasswordEntry.setOnEditorActionListener(this); + mPasswordEntry.addTextChangedListener(this); + + // Poke the wakelock any time the text is selected or modified + mPasswordEntry.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + mCallback.userActivity(); + } + }); + + // Set selected property on so the view can send accessibility events. + mPasswordEntry.setSelected(true); + + mSwitchImeButton = findViewById(R.id.switch_ime_button); + mSwitchImeButton.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + mCallback.userActivity(); // Leave the screen on a bit longer + // Do not show auxiliary subtypes in password lock screen. + mImm.showInputMethodPickerFromSystem(false /* showAuxiliarySubtypes */, + getContext().getDisplayId()); + } + }); + + View cancelBtn = findViewById(R.id.cancel_button); + if (cancelBtn != null) { + cancelBtn.setOnClickListener(view -> { + mCallback.reset(); + mCallback.onCancelClicked(); + }); + } + + // If there's more than one IME, enable the IME switcher button + updateSwitchImeButton(); + + // When we the current user is switching, InputMethodManagerService sometimes has not + // switched internal state yet here. As a quick workaround, we check the keyboard state + // again. + // TODO: Remove this workaround by ensuring such a race condition never happens. + postDelayed(new Runnable() { + @Override + public void run() { + updateSwitchImeButton(); + } + }, DELAY_MILLIS_TO_REEVALUATE_IME_SWITCH_ICON); } @Override @@ -129,6 +265,59 @@ public class KeyguardPasswordView extends KeyguardAbsKeyInputView { mPasswordEntryDisabler.setInputEnabled(enabled); } + /** + * Method adapted from com.android.inputmethod.latin.Utils + * + * @param imm The input method manager + * @param shouldIncludeAuxiliarySubtypes + * @return true if we have multiple IMEs to choose from + */ + private boolean hasMultipleEnabledIMEsOrSubtypes(InputMethodManager imm, + final boolean shouldIncludeAuxiliarySubtypes) { + final List<InputMethodInfo> enabledImis = + imm.getEnabledInputMethodListAsUser(KeyguardUpdateMonitor.getCurrentUser()); + + // Number of the filtered IMEs + int filteredImisCount = 0; + + for (InputMethodInfo imi : enabledImis) { + // We can return true immediately after we find two or more filtered IMEs. + if (filteredImisCount > 1) return true; + final List<InputMethodSubtype> subtypes = + imm.getEnabledInputMethodSubtypeList(imi, true); + // IMEs that have no subtypes should be counted. + if (subtypes.isEmpty()) { + ++filteredImisCount; + continue; + } + + int auxCount = 0; + for (InputMethodSubtype subtype : subtypes) { + if (subtype.isAuxiliary()) { + ++auxCount; + } + } + final int nonAuxCount = subtypes.size() - auxCount; + + // IMEs that have one or more non-auxiliary subtypes should be counted. + // If shouldIncludeAuxiliarySubtypes is true, IMEs that have two or more auxiliary + // subtypes should be counted as well. + if (nonAuxCount > 0 || (shouldIncludeAuxiliarySubtypes && auxCount > 1)) { + ++filteredImisCount; + continue; + } + } + + return filteredImisCount > 1 + // imm.getEnabledInputMethodSubtypeList(null, false) will return the current IME's enabled + // input method subtype (The current IME should be LatinIME.) + || imm.getEnabledInputMethodSubtypeList(null, false).size() > 1; + } + + @Override + public void showUsabilityHint() { + } + @Override public int getWrongPasswordStringId() { return R.string.kg_wrong_password; @@ -157,8 +346,45 @@ public class KeyguardPasswordView extends KeyguardAbsKeyInputView { } @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + if (mCallback != null) { + mCallback.userActivity(); + } + } + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + } + + @Override + public void afterTextChanged(Editable s) { + // Poor man's user edit detection, assuming empty text is programmatic and everything else + // is from the user. + if (!TextUtils.isEmpty(s)) { + onUserInput(); + } + } + + @Override + public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { + // Check if this was the result of hitting the enter key + final boolean isSoftImeEvent = event == null + && (actionId == EditorInfo.IME_NULL + || actionId == EditorInfo.IME_ACTION_DONE + || actionId == EditorInfo.IME_ACTION_NEXT); + final boolean isKeyboardEnterKey = event != null + && KeyEvent.isConfirmKey(event.getKeyCode()) + && event.getAction() == KeyEvent.ACTION_DOWN; + if (isSoftImeEvent || isKeyboardEnterKey) { + verifyPasswordAndUnlock(); + return true; + } + return false; + } + + @Override public CharSequence getTitle() { - return getResources().getString( + return getContext().getString( com.android.internal.R.string.keyguard_accessibility_password_unlock); } } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java deleted file mode 100644 index d34ea8c5e018..000000000000 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java +++ /dev/null @@ -1,275 +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.keyguard; - -import android.content.res.Resources; -import android.os.UserHandle; -import android.text.Editable; -import android.text.InputType; -import android.text.TextUtils; -import android.text.TextWatcher; -import android.text.method.TextKeyListener; -import android.view.KeyEvent; -import android.view.View; -import android.view.ViewGroup.MarginLayoutParams; -import android.view.inputmethod.EditorInfo; -import android.view.inputmethod.InputMethodInfo; -import android.view.inputmethod.InputMethodManager; -import android.view.inputmethod.InputMethodSubtype; -import android.widget.TextView; -import android.widget.TextView.OnEditorActionListener; - -import com.android.internal.util.LatencyTracker; -import com.android.internal.widget.LockPatternUtils; -import com.android.keyguard.KeyguardSecurityModel.SecurityMode; -import com.android.systemui.R; -import com.android.systemui.dagger.qualifiers.Main; -import com.android.systemui.util.concurrency.DelayableExecutor; - -import java.util.List; - -public class KeyguardPasswordViewController - extends KeyguardAbsKeyInputViewController<KeyguardPasswordView> { - - private static final int DELAY_MILLIS_TO_REEVALUATE_IME_SWITCH_ICON = 500; // 500ms - - private final KeyguardSecurityCallback mKeyguardSecurityCallback; - private final InputMethodManager mInputMethodManager; - private final DelayableExecutor mMainExecutor; - private final boolean mShowImeAtScreenOn; - private TextView mPasswordEntry; - private View mSwitchImeButton; - - private final OnEditorActionListener mOnEditorActionListener = (v, actionId, event) -> { - // Check if this was the result of hitting the enter key - final boolean isSoftImeEvent = event == null - && (actionId == EditorInfo.IME_NULL - || actionId == EditorInfo.IME_ACTION_DONE - || actionId == EditorInfo.IME_ACTION_NEXT); - final boolean isKeyboardEnterKey = event != null - && KeyEvent.isConfirmKey(event.getKeyCode()) - && event.getAction() == KeyEvent.ACTION_DOWN; - if (isSoftImeEvent || isKeyboardEnterKey) { - verifyPasswordAndUnlock(); - return true; - } - return false; - }; - - private final TextWatcher mTextWatcher = new TextWatcher() { - @Override - public void beforeTextChanged(CharSequence s, int start, int count, int after) { - mKeyguardSecurityCallback.userActivity(); - } - - @Override - public void onTextChanged(CharSequence s, int start, int before, int count) { - } - - @Override - public void afterTextChanged(Editable s) { - if (!TextUtils.isEmpty(s)) { - onUserInput(); - } - } - }; - - protected KeyguardPasswordViewController(KeyguardPasswordView view, - KeyguardUpdateMonitor keyguardUpdateMonitor, - SecurityMode securityMode, - LockPatternUtils lockPatternUtils, - KeyguardSecurityCallback keyguardSecurityCallback, - KeyguardMessageAreaController.Factory messageAreaControllerFactory, - LatencyTracker latencyTracker, - InputMethodManager inputMethodManager, - @Main DelayableExecutor mainExecutor, - @Main Resources resources) { - super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback, - messageAreaControllerFactory, latencyTracker); - mKeyguardSecurityCallback = keyguardSecurityCallback; - mInputMethodManager = inputMethodManager; - mMainExecutor = mainExecutor; - mShowImeAtScreenOn = resources.getBoolean(R.bool.kg_show_ime_at_screen_on); - mPasswordEntry = mView.findViewById(mView.getPasswordTextViewId()); - mSwitchImeButton = mView.findViewById(R.id.switch_ime_button); - } - - @Override - protected void onViewAttached() { - super.onViewAttached(); - mPasswordEntry.setTextOperationUser(UserHandle.of(KeyguardUpdateMonitor.getCurrentUser())); - mPasswordEntry.setKeyListener(TextKeyListener.getInstance()); - mPasswordEntry.setInputType(InputType.TYPE_CLASS_TEXT - | InputType.TYPE_TEXT_VARIATION_PASSWORD); - - // Set selected property on so the view can send accessibility events. - mPasswordEntry.setSelected(true); - mPasswordEntry.setOnEditorActionListener(mOnEditorActionListener); - mPasswordEntry.addTextChangedListener(mTextWatcher); - // Poke the wakelock any time the text is selected or modified - mPasswordEntry.setOnClickListener(v -> mKeyguardSecurityCallback.userActivity()); - - mSwitchImeButton.setOnClickListener(v -> { - mKeyguardSecurityCallback.userActivity(); // Leave the screen on a bit longer - // Do not show auxiliary subtypes in password lock screen. - mInputMethodManager.showInputMethodPickerFromSystem(false, - mView.getContext().getDisplayId()); - }); - - View cancelBtn = mView.findViewById(R.id.cancel_button); - if (cancelBtn != null) { - cancelBtn.setOnClickListener(view -> { - mKeyguardSecurityCallback.reset(); - mKeyguardSecurityCallback.onCancelClicked(); - }); - } - - // If there's more than one IME, enable the IME switcher button - updateSwitchImeButton(); - - // When we the current user is switching, InputMethodManagerService sometimes has not - // switched internal state yet here. As a quick workaround, we check the keyboard state - // again. - // TODO: Remove this workaround by ensuring such a race condition never happens. - mMainExecutor.executeDelayed( - this::updateSwitchImeButton, DELAY_MILLIS_TO_REEVALUATE_IME_SWITCH_ICON); - } - - @Override - protected void onViewDetached() { - super.onViewDetached(); - mPasswordEntry.setOnEditorActionListener(null); - } - - @Override - public boolean needsInput() { - return true; - } - - @Override - void resetState() { - mPasswordEntry.setTextOperationUser(UserHandle.of(KeyguardUpdateMonitor.getCurrentUser())); - mMessageAreaController.setMessage(""); - final boolean wasDisabled = mPasswordEntry.isEnabled(); - mView.setPasswordEntryEnabled(true); - mView.setPasswordEntryInputEnabled(true); - // Don't call showSoftInput when PasswordEntry is invisible or in pausing stage. - if (!mResumed || !mPasswordEntry.isVisibleToUser()) { - return; - } - if (wasDisabled) { - mInputMethodManager.showSoftInput(mPasswordEntry, InputMethodManager.SHOW_IMPLICIT); - } - } - - @Override - public void onResume(int reason) { - super.onResume(reason); - // Wait a bit to focus the field so the focusable flag on the window is already set then. - mMainExecutor.execute(() -> { - if (mView.isShown() && mPasswordEntry.isEnabled()) { - mPasswordEntry.requestFocus(); - if (reason != KeyguardSecurityView.SCREEN_ON || mShowImeAtScreenOn) { - mInputMethodManager.showSoftInput( - mPasswordEntry, InputMethodManager.SHOW_IMPLICIT); - } - } - }); - } - - @Override - public void onPause() { - super.onPause(); - mInputMethodManager.hideSoftInputFromWindow(mView.getWindowToken(), 0); - } - - @Override - public void onStartingToHide() { - mInputMethodManager.hideSoftInputFromWindow(mView.getWindowToken(), 0); - } - - private void updateSwitchImeButton() { - // If there's more than one IME, enable the IME switcher button - final boolean wasVisible = mSwitchImeButton.getVisibility() == View.VISIBLE; - final boolean shouldBeVisible = hasMultipleEnabledIMEsOrSubtypes( - mInputMethodManager, false); - if (wasVisible != shouldBeVisible) { - mSwitchImeButton.setVisibility(shouldBeVisible ? View.VISIBLE : View.GONE); - } - - // TODO: Check if we still need this hack. - // If no icon is visible, reset the start margin on the password field so the text is - // still centered. - if (mSwitchImeButton.getVisibility() != View.VISIBLE) { - android.view.ViewGroup.LayoutParams params = mPasswordEntry.getLayoutParams(); - if (params instanceof MarginLayoutParams) { - final MarginLayoutParams mlp = (MarginLayoutParams) params; - mlp.setMarginStart(0); - mPasswordEntry.setLayoutParams(params); - } - } - } - - /** - * Method adapted from com.android.inputmethod.latin.Utils - * - * @param imm The input method manager - * @param shouldIncludeAuxiliarySubtypes - * @return true if we have multiple IMEs to choose from - */ - private boolean hasMultipleEnabledIMEsOrSubtypes(InputMethodManager imm, - final boolean shouldIncludeAuxiliarySubtypes) { - final List<InputMethodInfo> enabledImis = - imm.getEnabledInputMethodListAsUser(KeyguardUpdateMonitor.getCurrentUser()); - - // Number of the filtered IMEs - int filteredImisCount = 0; - - for (InputMethodInfo imi : enabledImis) { - // We can return true immediately after we find two or more filtered IMEs. - if (filteredImisCount > 1) return true; - final List<InputMethodSubtype> subtypes = - imm.getEnabledInputMethodSubtypeList(imi, true); - // IMEs that have no subtypes should be counted. - if (subtypes.isEmpty()) { - ++filteredImisCount; - continue; - } - - int auxCount = 0; - for (InputMethodSubtype subtype : subtypes) { - if (subtype.isAuxiliary()) { - ++auxCount; - } - } - final int nonAuxCount = subtypes.size() - auxCount; - - // IMEs that have one or more non-auxiliary subtypes should be counted. - // If shouldIncludeAuxiliarySubtypes is true, IMEs that have two or more auxiliary - // subtypes should be counted as well. - if (nonAuxCount > 0 || (shouldIncludeAuxiliarySubtypes && auxCount > 1)) { - ++filteredImisCount; - continue; - } - } - - return filteredImisCount > 1 - // imm.getEnabledInputMethodSubtypeList(null, false) will return the current IME's - //enabled input method subtype (The current IME should be LatinIME.) - || imm.getEnabledInputMethodSubtypeList(null, false).size() > 1; - } -} diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java index bdcf467c2456..c4a9fcb45284 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java @@ -15,39 +15,62 @@ */ package com.android.keyguard; +import static com.android.internal.util.LatencyTracker.ACTION_CHECK_CREDENTIAL; +import static com.android.internal.util.LatencyTracker.ACTION_CHECK_CREDENTIAL_UNLOCKED; + import android.content.Context; +import android.content.res.ColorStateList; import android.graphics.Rect; +import android.os.AsyncTask; +import android.os.CountDownTimer; import android.os.SystemClock; import android.text.TextUtils; import android.util.AttributeSet; +import android.util.Log; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.view.animation.AnimationUtils; import android.view.animation.Interpolator; +import android.widget.LinearLayout; +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.LatencyTracker; +import com.android.internal.widget.LockPatternChecker; +import com.android.internal.widget.LockPatternUtils; import com.android.internal.widget.LockPatternView; +import com.android.internal.widget.LockscreenCredential; import com.android.settingslib.animation.AppearAnimationCreator; import com.android.settingslib.animation.AppearAnimationUtils; import com.android.settingslib.animation.DisappearAnimationUtils; +import com.android.systemui.Dependency; import com.android.systemui.R; -public class KeyguardPatternView extends KeyguardInputView - implements AppearAnimationCreator<LockPatternView.CellState> { +import java.util.List; + +public class KeyguardPatternView extends LinearLayout implements KeyguardSecurityView, + AppearAnimationCreator<LockPatternView.CellState>, + EmergencyButton.EmergencyButtonCallback { private static final String TAG = "SecurityPatternView"; private static final boolean DEBUG = KeyguardConstants.DEBUG; + // how long before we clear the wrong pattern + private static final int PATTERN_CLEAR_TIMEOUT_MS = 2000; // how long we stay awake after each key beyond MIN_PATTERN_BEFORE_POKE_WAKELOCK private static final int UNLOCK_PATTERN_WAKE_INTERVAL_MS = 7000; + // how many cells the user has to cross before we poke the wakelock + private static final int MIN_PATTERN_BEFORE_POKE_WAKELOCK = 2; + // How much we scale up the duration of the disappear animation when the current user is locked public static final float DISAPPEAR_MULTIPLIER_LOCKED = 1.5f; // Extra padding, in pixels, that should eat touch events. private static final int PATTERNS_TOUCH_AREA_EXTENSION = 40; + private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; private final AppearAnimationUtils mAppearAnimationUtils; private final DisappearAnimationUtils mDisappearAnimationUtils; private final DisappearAnimationUtils mDisappearAnimationUtilsLocked; @@ -55,7 +78,11 @@ public class KeyguardPatternView extends KeyguardInputView private final Rect mTempRect = new Rect(); private final Rect mLockPatternScreenBounds = new Rect(); + private CountDownTimer mCountdownTimer = null; + private LockPatternUtils mLockPatternUtils; + private AsyncTask<?, ?, ?> mPendingLockCheck; private LockPatternView mLockPatternView; + private KeyguardSecurityCallback mCallback; /** * Keeps track of the last time we poked the wake lock during dispatching of the touch event. @@ -65,9 +92,26 @@ public class KeyguardPatternView extends KeyguardInputView */ private long mLastPokeTime = -UNLOCK_PATTERN_WAKE_INTERVAL_MS; + /** + * Useful for clearing out the wrong pattern after a delay + */ + private Runnable mCancelPatternRunnable = new Runnable() { + @Override + public void run() { + mLockPatternView.clearPattern(); + } + }; + @VisibleForTesting KeyguardMessageArea mSecurityMessageDisplay; private View mEcaView; private ViewGroup mContainer; + private int mDisappearYTranslation; + + enum FooterMode { + Normal, + ForgotLockPattern, + VerifyUnlocked + } public KeyguardPatternView(Context context) { this(context, null); @@ -75,6 +119,7 @@ public class KeyguardPatternView extends KeyguardInputView public KeyguardPatternView(Context context, AttributeSet attrs) { super(context, attrs); + mKeyguardUpdateMonitor = Dependency.get(KeyguardUpdateMonitor.class); mAppearAnimationUtils = new AppearAnimationUtils(context, AppearAnimationUtils.DEFAULT_APPEAR_DURATION, 1.5f /* translationScale */, 2.0f /* delayScale */, AnimationUtils.loadInterpolator( @@ -87,16 +132,50 @@ public class KeyguardPatternView extends KeyguardInputView (long) (125 * DISAPPEAR_MULTIPLIER_LOCKED), 1.2f /* translationScale */, 0.6f /* delayScale */, AnimationUtils.loadInterpolator( mContext, android.R.interpolator.fast_out_linear_in)); + mDisappearYTranslation = getResources().getDimensionPixelSize( + R.dimen.disappear_y_translation); + } + + @Override + public void setKeyguardCallback(KeyguardSecurityCallback callback) { + mCallback = callback; + } + + @Override + public void setLockPatternUtils(LockPatternUtils utils) { + mLockPatternUtils = utils; } @Override protected void onFinishInflate() { super.onFinishInflate(); + mLockPatternUtils = mLockPatternUtils == null + ? new LockPatternUtils(mContext) : mLockPatternUtils; mLockPatternView = findViewById(R.id.lockPatternView); + mLockPatternView.setSaveEnabled(false); + mLockPatternView.setOnPatternListener(new UnlockPatternListener()); + mLockPatternView.setInStealthMode(!mLockPatternUtils.isVisiblePatternEnabled( + KeyguardUpdateMonitor.getCurrentUser())); + + // vibrate mode will be the same for the life of this screen + mLockPatternView.setTactileFeedbackEnabled(mLockPatternUtils.isTactileFeedbackEnabled()); mEcaView = findViewById(R.id.keyguard_selector_fade_container); mContainer = findViewById(R.id.container); + + EmergencyButton button = findViewById(R.id.emergency_call_button); + if (button != null) { + button.setCallback(this); + } + + View cancelBtn = findViewById(R.id.cancel_button); + if (cancelBtn != null) { + cancelBtn.setOnClickListener(view -> { + mCallback.reset(); + mCallback.onCancelClicked(); + }); + } } @Override @@ -106,6 +185,11 @@ public class KeyguardPatternView extends KeyguardInputView } @Override + public void onEmergencyButtonClickedWhenInCall() { + mCallback.reset(); + } + + @Override public boolean onTouchEvent(MotionEvent ev) { boolean result = super.onTouchEvent(ev); // as long as the user is entering a pattern (i.e sending a touch event that was handled @@ -133,11 +217,248 @@ public class KeyguardPatternView extends KeyguardInputView } @Override - boolean disallowInterceptTouch(MotionEvent event) { + public void reset() { + // reset lock pattern + mLockPatternView.setInStealthMode(!mLockPatternUtils.isVisiblePatternEnabled( + KeyguardUpdateMonitor.getCurrentUser())); + mLockPatternView.enableInput(); + mLockPatternView.setEnabled(true); + mLockPatternView.clearPattern(); + + if (mSecurityMessageDisplay == null) { + return; + } + + // if the user is currently locked out, enforce it. + long deadline = mLockPatternUtils.getLockoutAttemptDeadline( + KeyguardUpdateMonitor.getCurrentUser()); + if (deadline != 0) { + handleAttemptLockout(deadline); + } else { + displayDefaultSecurityMessage(); + } + } + + private void displayDefaultSecurityMessage() { + if (mSecurityMessageDisplay != null) { + mSecurityMessageDisplay.setMessage(""); + } + } + + @Override + public void showUsabilityHint() { + } + + @Override + public boolean disallowInterceptTouch(MotionEvent event) { return !mLockPatternView.isEmpty() || mLockPatternScreenBounds.contains((int) event.getRawX(), (int) event.getRawY()); } + /** TODO: hook this up */ + public void cleanUp() { + if (DEBUG) Log.v(TAG, "Cleanup() called on " + this); + mLockPatternUtils = null; + mLockPatternView.setOnPatternListener(null); + } + + private class UnlockPatternListener implements LockPatternView.OnPatternListener { + + @Override + public void onPatternStart() { + mLockPatternView.removeCallbacks(mCancelPatternRunnable); + mSecurityMessageDisplay.setMessage(""); + } + + @Override + public void onPatternCleared() { + } + + @Override + public void onPatternCellAdded(List<LockPatternView.Cell> pattern) { + mCallback.userActivity(); + mCallback.onUserInput(); + } + + @Override + public void onPatternDetected(final List<LockPatternView.Cell> pattern) { + mKeyguardUpdateMonitor.setCredentialAttempted(); + mLockPatternView.disableInput(); + if (mPendingLockCheck != null) { + mPendingLockCheck.cancel(false); + } + + final int userId = KeyguardUpdateMonitor.getCurrentUser(); + if (pattern.size() < LockPatternUtils.MIN_PATTERN_REGISTER_FAIL) { + mLockPatternView.enableInput(); + onPatternChecked(userId, false, 0, false /* not valid - too short */); + return; + } + + if (LatencyTracker.isEnabled(mContext)) { + LatencyTracker.getInstance(mContext).onActionStart(ACTION_CHECK_CREDENTIAL); + LatencyTracker.getInstance(mContext).onActionStart(ACTION_CHECK_CREDENTIAL_UNLOCKED); + } + mPendingLockCheck = LockPatternChecker.checkCredential( + mLockPatternUtils, + LockscreenCredential.createPattern(pattern), + userId, + new LockPatternChecker.OnCheckCallback() { + + @Override + public void onEarlyMatched() { + if (LatencyTracker.isEnabled(mContext)) { + LatencyTracker.getInstance(mContext).onActionEnd( + ACTION_CHECK_CREDENTIAL); + } + onPatternChecked(userId, true /* matched */, 0 /* timeoutMs */, + true /* isValidPattern */); + } + + @Override + public void onChecked(boolean matched, int timeoutMs) { + if (LatencyTracker.isEnabled(mContext)) { + LatencyTracker.getInstance(mContext).onActionEnd( + ACTION_CHECK_CREDENTIAL_UNLOCKED); + } + mLockPatternView.enableInput(); + mPendingLockCheck = null; + if (!matched) { + onPatternChecked(userId, false /* matched */, timeoutMs, + true /* isValidPattern */); + } + } + + @Override + public void onCancelled() { + // We already got dismissed with the early matched callback, so we + // cancelled the check. However, we still need to note down the latency. + if (LatencyTracker.isEnabled(mContext)) { + LatencyTracker.getInstance(mContext).onActionEnd( + ACTION_CHECK_CREDENTIAL_UNLOCKED); + } + } + }); + if (pattern.size() > MIN_PATTERN_BEFORE_POKE_WAKELOCK) { + mCallback.userActivity(); + mCallback.onUserInput(); + } + } + + private void onPatternChecked(int userId, boolean matched, int timeoutMs, + boolean isValidPattern) { + boolean dismissKeyguard = KeyguardUpdateMonitor.getCurrentUser() == userId; + if (matched) { + mCallback.reportUnlockAttempt(userId, true, 0); + if (dismissKeyguard) { + mLockPatternView.setDisplayMode(LockPatternView.DisplayMode.Correct); + mCallback.dismiss(true, userId); + } + } else { + mLockPatternView.setDisplayMode(LockPatternView.DisplayMode.Wrong); + if (isValidPattern) { + mCallback.reportUnlockAttempt(userId, false, timeoutMs); + if (timeoutMs > 0) { + long deadline = mLockPatternUtils.setLockoutAttemptDeadline( + userId, timeoutMs); + handleAttemptLockout(deadline); + } + } + if (timeoutMs == 0) { + mSecurityMessageDisplay.setMessage(R.string.kg_wrong_pattern); + mLockPatternView.postDelayed(mCancelPatternRunnable, PATTERN_CLEAR_TIMEOUT_MS); + } + } + } + } + + private void handleAttemptLockout(long elapsedRealtimeDeadline) { + mLockPatternView.clearPattern(); + mLockPatternView.setEnabled(false); + final long elapsedRealtime = SystemClock.elapsedRealtime(); + final long secondsInFuture = (long) Math.ceil( + (elapsedRealtimeDeadline - elapsedRealtime) / 1000.0); + mCountdownTimer = new CountDownTimer(secondsInFuture * 1000, 1000) { + + @Override + public void onTick(long millisUntilFinished) { + final int secondsRemaining = (int) Math.round(millisUntilFinished / 1000.0); + mSecurityMessageDisplay.setMessage(mContext.getResources().getQuantityString( + R.plurals.kg_too_many_failed_attempts_countdown, + secondsRemaining, secondsRemaining)); + } + + @Override + public void onFinish() { + mLockPatternView.setEnabled(true); + displayDefaultSecurityMessage(); + } + + }.start(); + } + + @Override + public boolean needsInput() { + return false; + } + + @Override + public void onPause() { + if (mCountdownTimer != null) { + mCountdownTimer.cancel(); + mCountdownTimer = null; + } + if (mPendingLockCheck != null) { + mPendingLockCheck.cancel(false); + mPendingLockCheck = null; + } + displayDefaultSecurityMessage(); + } + + @Override + public void onResume(int reason) { + } + + @Override + public KeyguardSecurityCallback getCallback() { + return mCallback; + } + + @Override + public void showPromptReason(int reason) { + switch (reason) { + case PROMPT_REASON_RESTART: + mSecurityMessageDisplay.setMessage(R.string.kg_prompt_reason_restart_pattern); + break; + case PROMPT_REASON_TIMEOUT: + mSecurityMessageDisplay.setMessage(R.string.kg_prompt_reason_timeout_pattern); + break; + case PROMPT_REASON_DEVICE_ADMIN: + mSecurityMessageDisplay.setMessage(R.string.kg_prompt_reason_device_admin); + break; + case PROMPT_REASON_USER_REQUEST: + mSecurityMessageDisplay.setMessage(R.string.kg_prompt_reason_user_request); + break; + case PROMPT_REASON_PREPARE_FOR_UPDATE: + mSecurityMessageDisplay.setMessage(R.string.kg_prompt_reason_timeout_pattern); + break; + case PROMPT_REASON_NONE: + break; + default: + mSecurityMessageDisplay.setMessage(R.string.kg_prompt_reason_timeout_pattern); + break; + } + } + + @Override + public void showMessage(CharSequence message, ColorStateList colorState) { + if (colorState != null) { + mSecurityMessageDisplay.setNextMessageColor(colorState); + } + mSecurityMessageDisplay.setMessage(message); + } + + @Override public void startAppearAnimation() { enableClipping(false); setAlpha(1f); @@ -146,7 +467,12 @@ public class KeyguardPatternView extends KeyguardInputView 0, mAppearAnimationUtils.getInterpolator()); mAppearAnimationUtils.startAnimation2d( mLockPatternView.getCellStates(), - () -> enableClipping(true), + new Runnable() { + @Override + public void run() { + enableClipping(true); + } + }, this); if (!TextUtils.isEmpty(mSecurityMessageDisplay.getText())) { mAppearAnimationUtils.createAnimation(mSecurityMessageDisplay, 0, @@ -158,9 +484,11 @@ public class KeyguardPatternView extends KeyguardInputView } } - public boolean startDisappearAnimation(boolean needsSlowUnlockTransition, - final Runnable finishRunnable) { - float durationMultiplier = needsSlowUnlockTransition ? DISAPPEAR_MULTIPLIER_LOCKED : 1f; + @Override + public boolean startDisappearAnimation(final Runnable finishRunnable) { + float durationMultiplier = mKeyguardUpdateMonitor.needsSlowUnlockTransition() + ? DISAPPEAR_MULTIPLIER_LOCKED + : 1f; mLockPatternView.clearPattern(); enableClipping(false); setTranslationY(0); @@ -169,8 +497,10 @@ public class KeyguardPatternView extends KeyguardInputView -mDisappearAnimationUtils.getStartTranslation(), mDisappearAnimationUtils.getInterpolator()); - DisappearAnimationUtils disappearAnimationUtils = needsSlowUnlockTransition - ? mDisappearAnimationUtilsLocked : mDisappearAnimationUtils; + DisappearAnimationUtils disappearAnimationUtils = mKeyguardUpdateMonitor + .needsSlowUnlockTransition() + ? mDisappearAnimationUtilsLocked + : mDisappearAnimationUtils; disappearAnimationUtils.startAnimation2d(mLockPatternView.getCellStates(), () -> { enableClipping(true); @@ -219,7 +549,7 @@ public class KeyguardPatternView extends KeyguardInputView @Override public CharSequence getTitle() { - return getResources().getString( + return getContext().getString( com.android.internal.R.string.keyguard_accessibility_pattern_unlock); } } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java deleted file mode 100644 index 3db9db7be00c..000000000000 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java +++ /dev/null @@ -1,349 +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.keyguard; - -import static com.android.internal.util.LatencyTracker.ACTION_CHECK_CREDENTIAL; -import static com.android.internal.util.LatencyTracker.ACTION_CHECK_CREDENTIAL_UNLOCKED; - -import android.content.res.ColorStateList; -import android.os.AsyncTask; -import android.os.CountDownTimer; -import android.os.SystemClock; -import android.view.View; - -import com.android.internal.util.LatencyTracker; -import com.android.internal.widget.LockPatternChecker; -import com.android.internal.widget.LockPatternUtils; -import com.android.internal.widget.LockPatternView; -import com.android.internal.widget.LockPatternView.Cell; -import com.android.internal.widget.LockscreenCredential; -import com.android.keyguard.EmergencyButton.EmergencyButtonCallback; -import com.android.keyguard.KeyguardSecurityModel.SecurityMode; -import com.android.systemui.R; - -import java.util.List; - -public class KeyguardPatternViewController - extends KeyguardInputViewController<KeyguardPatternView> { - - // how many cells the user has to cross before we poke the wakelock - private static final int MIN_PATTERN_BEFORE_POKE_WAKELOCK = 2; - - // how long before we clear the wrong pattern - private static final int PATTERN_CLEAR_TIMEOUT_MS = 2000; - - private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; - private final LockPatternUtils mLockPatternUtils; - private final LatencyTracker mLatencyTracker; - private final KeyguardMessageAreaController.Factory mMessageAreaControllerFactory; - - private KeyguardMessageAreaController mMessageAreaController; - private LockPatternView mLockPatternView; - private CountDownTimer mCountdownTimer; - private AsyncTask<?, ?, ?> mPendingLockCheck; - - private EmergencyButtonCallback mEmergencyButtonCallback = new EmergencyButtonCallback() { - @Override - public void onEmergencyButtonClickedWhenInCall() { - getKeyguardSecurityCallback().reset(); - } - }; - - /** - * Useful for clearing out the wrong pattern after a delay - */ - private Runnable mCancelPatternRunnable = new Runnable() { - @Override - public void run() { - mLockPatternView.clearPattern(); - } - }; - - private class UnlockPatternListener implements LockPatternView.OnPatternListener { - - @Override - public void onPatternStart() { - mLockPatternView.removeCallbacks(mCancelPatternRunnable); - mMessageAreaController.setMessage(""); - } - - @Override - public void onPatternCleared() { - } - - @Override - public void onPatternCellAdded(List<Cell> pattern) { - getKeyguardSecurityCallback().userActivity(); - getKeyguardSecurityCallback().onUserInput(); - } - - @Override - public void onPatternDetected(final List<LockPatternView.Cell> pattern) { - mKeyguardUpdateMonitor.setCredentialAttempted(); - mLockPatternView.disableInput(); - if (mPendingLockCheck != null) { - mPendingLockCheck.cancel(false); - } - - final int userId = KeyguardUpdateMonitor.getCurrentUser(); - if (pattern.size() < LockPatternUtils.MIN_PATTERN_REGISTER_FAIL) { - mLockPatternView.enableInput(); - onPatternChecked(userId, false, 0, false /* not valid - too short */); - return; - } - - mLatencyTracker.onActionStart(ACTION_CHECK_CREDENTIAL); - mLatencyTracker.onActionStart(ACTION_CHECK_CREDENTIAL_UNLOCKED); - mPendingLockCheck = LockPatternChecker.checkCredential( - mLockPatternUtils, - LockscreenCredential.createPattern(pattern), - userId, - new LockPatternChecker.OnCheckCallback() { - - @Override - public void onEarlyMatched() { - mLatencyTracker.onActionEnd(ACTION_CHECK_CREDENTIAL); - onPatternChecked(userId, true /* matched */, 0 /* timeoutMs */, - true /* isValidPattern */); - } - - @Override - public void onChecked(boolean matched, int timeoutMs) { - mLatencyTracker.onActionEnd(ACTION_CHECK_CREDENTIAL_UNLOCKED); - mLockPatternView.enableInput(); - mPendingLockCheck = null; - if (!matched) { - onPatternChecked(userId, false /* matched */, timeoutMs, - true /* isValidPattern */); - } - } - - @Override - public void onCancelled() { - // We already got dismissed with the early matched callback, so we - // cancelled the check. However, we still need to note down the latency. - mLatencyTracker.onActionEnd(ACTION_CHECK_CREDENTIAL_UNLOCKED); - } - }); - if (pattern.size() > MIN_PATTERN_BEFORE_POKE_WAKELOCK) { - getKeyguardSecurityCallback().userActivity(); - getKeyguardSecurityCallback().onUserInput(); - } - } - - private void onPatternChecked(int userId, boolean matched, int timeoutMs, - boolean isValidPattern) { - boolean dismissKeyguard = KeyguardUpdateMonitor.getCurrentUser() == userId; - if (matched) { - getKeyguardSecurityCallback().reportUnlockAttempt(userId, true, 0); - if (dismissKeyguard) { - mLockPatternView.setDisplayMode(LockPatternView.DisplayMode.Correct); - getKeyguardSecurityCallback().dismiss(true, userId); - } - } else { - mLockPatternView.setDisplayMode(LockPatternView.DisplayMode.Wrong); - if (isValidPattern) { - getKeyguardSecurityCallback().reportUnlockAttempt(userId, false, timeoutMs); - if (timeoutMs > 0) { - long deadline = mLockPatternUtils.setLockoutAttemptDeadline( - userId, timeoutMs); - handleAttemptLockout(deadline); - } - } - if (timeoutMs == 0) { - mMessageAreaController.setMessage(R.string.kg_wrong_pattern); - mLockPatternView.postDelayed(mCancelPatternRunnable, PATTERN_CLEAR_TIMEOUT_MS); - } - } - } - } - - protected KeyguardPatternViewController(KeyguardPatternView view, - KeyguardUpdateMonitor keyguardUpdateMonitor, - SecurityMode securityMode, - LockPatternUtils lockPatternUtils, - KeyguardSecurityCallback keyguardSecurityCallback, - LatencyTracker latencyTracker, - KeyguardMessageAreaController.Factory messageAreaControllerFactory) { - super(view, securityMode, keyguardSecurityCallback); - mKeyguardUpdateMonitor = keyguardUpdateMonitor; - mLockPatternUtils = lockPatternUtils; - mLatencyTracker = latencyTracker; - mMessageAreaControllerFactory = messageAreaControllerFactory; - KeyguardMessageArea kma = KeyguardMessageArea.findSecurityMessageDisplay(mView); - mMessageAreaController = mMessageAreaControllerFactory.create(kma); - mLockPatternView = mView.findViewById(R.id.lockPatternView); - } - - @Override - public void init() { - super.init(); - mMessageAreaController.init(); - } - - @Override - protected void onViewAttached() { - mLockPatternView.setOnPatternListener(new UnlockPatternListener()); - mLockPatternView.setSaveEnabled(false); - mLockPatternView.setInStealthMode(!mLockPatternUtils.isVisiblePatternEnabled( - KeyguardUpdateMonitor.getCurrentUser())); - // vibrate mode will be the same for the life of this screen - mLockPatternView.setTactileFeedbackEnabled(mLockPatternUtils.isTactileFeedbackEnabled()); - - EmergencyButton button = mView.findViewById(R.id.emergency_call_button); - if (button != null) { - button.setCallback(mEmergencyButtonCallback); - } - - View cancelBtn = mView.findViewById(R.id.cancel_button); - if (cancelBtn != null) { - cancelBtn.setOnClickListener(view -> { - getKeyguardSecurityCallback().reset(); - getKeyguardSecurityCallback().onCancelClicked(); - }); - } - } - - @Override - protected void onViewDetached() { - super.onViewDetached(); - mLockPatternView.setOnPatternListener(null); - EmergencyButton button = mView.findViewById(R.id.emergency_call_button); - if (button != null) { - button.setCallback(null); - } - View cancelBtn = mView.findViewById(R.id.cancel_button); - if (cancelBtn != null) { - cancelBtn.setOnClickListener(null); - } - } - - @Override - public void reset() { - // reset lock pattern - mLockPatternView.setInStealthMode(!mLockPatternUtils.isVisiblePatternEnabled( - KeyguardUpdateMonitor.getCurrentUser())); - mLockPatternView.enableInput(); - mLockPatternView.setEnabled(true); - mLockPatternView.clearPattern(); - - // if the user is currently locked out, enforce it. - long deadline = mLockPatternUtils.getLockoutAttemptDeadline( - KeyguardUpdateMonitor.getCurrentUser()); - if (deadline != 0) { - handleAttemptLockout(deadline); - } else { - displayDefaultSecurityMessage(); - } - } - - @Override - public void onPause() { - super.onPause(); - - if (mCountdownTimer != null) { - mCountdownTimer.cancel(); - mCountdownTimer = null; - } - - if (mPendingLockCheck != null) { - mPendingLockCheck.cancel(false); - mPendingLockCheck = null; - } - displayDefaultSecurityMessage(); - } - - @Override - public boolean needsInput() { - return false; - } - - @Override - public void showPromptReason(int reason) { - /// TODO: move all this logic into the MessageAreaController? - switch (reason) { - case PROMPT_REASON_RESTART: - mMessageAreaController.setMessage(R.string.kg_prompt_reason_restart_pattern); - break; - case PROMPT_REASON_TIMEOUT: - mMessageAreaController.setMessage(R.string.kg_prompt_reason_timeout_pattern); - break; - case PROMPT_REASON_DEVICE_ADMIN: - mMessageAreaController.setMessage(R.string.kg_prompt_reason_device_admin); - break; - case PROMPT_REASON_USER_REQUEST: - mMessageAreaController.setMessage(R.string.kg_prompt_reason_user_request); - break; - case PROMPT_REASON_PREPARE_FOR_UPDATE: - mMessageAreaController.setMessage(R.string.kg_prompt_reason_timeout_pattern); - break; - case PROMPT_REASON_NONE: - break; - default: - mMessageAreaController.setMessage(R.string.kg_prompt_reason_timeout_pattern); - break; - } - } - - @Override - public void showMessage(CharSequence message, ColorStateList colorState) { - if (colorState != null) { - mMessageAreaController.setNextMessageColor(colorState); - } - mMessageAreaController.setMessage(message); - } - - @Override - public void startAppearAnimation() { - super.startAppearAnimation(); - } - - @Override - public boolean startDisappearAnimation(Runnable finishRunnable) { - return mView.startDisappearAnimation( - mKeyguardUpdateMonitor.needsSlowUnlockTransition(), finishRunnable); - } - - private void displayDefaultSecurityMessage() { - mMessageAreaController.setMessage(""); - } - - private void handleAttemptLockout(long elapsedRealtimeDeadline) { - mLockPatternView.clearPattern(); - mLockPatternView.setEnabled(false); - final long elapsedRealtime = SystemClock.elapsedRealtime(); - final long secondsInFuture = (long) Math.ceil( - (elapsedRealtimeDeadline - elapsedRealtime) / 1000.0); - mCountdownTimer = new CountDownTimer(secondsInFuture * 1000, 1000) { - - @Override - public void onTick(long millisUntilFinished) { - final int secondsRemaining = (int) Math.round(millisUntilFinished / 1000.0); - mMessageAreaController.setMessage(mView.getResources().getQuantityString( - R.plurals.kg_too_many_failed_attempts_countdown, - secondsRemaining, secondsRemaining)); - } - - @Override - public void onFinish() { - mLockPatternView.setEnabled(true); - displayDefaultSecurityMessage(); - } - - }.start(); - } -} diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java index 7fa43116a7b1..c7f27cf8a71a 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java @@ -16,17 +16,11 @@ package com.android.keyguard; -import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_DEVICE_ADMIN; -import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_NONE; -import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_PREPARE_FOR_UPDATE; -import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_RESTART; -import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_TIMEOUT; -import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_USER_REQUEST; - import android.content.Context; import android.graphics.Rect; import android.util.AttributeSet; import android.view.KeyEvent; +import android.view.MotionEvent; import android.view.View; import com.android.internal.widget.LockscreenCredential; @@ -35,12 +29,22 @@ import com.android.systemui.R; /** * A Pin based Keyguard input view */ -public abstract class KeyguardPinBasedInputView extends KeyguardAbsKeyInputView { +public abstract class KeyguardPinBasedInputView extends KeyguardAbsKeyInputView + implements View.OnKeyListener, View.OnTouchListener { protected PasswordTextView mPasswordEntry; private View mOkButton; private View mDeleteButton; - private View[] mButtons = new View[10]; + private View mButton0; + private View mButton1; + private View mButton2; + private View mButton3; + private View mButton4; + private View mButton5; + private View mButton6; + private View mButton7; + private View mButton8; + private View mButton9; public KeyguardPinBasedInputView(Context context) { this(context, null); @@ -58,6 +62,7 @@ public abstract class KeyguardPinBasedInputView extends KeyguardAbsKeyInputView @Override protected void resetState() { + setPasswordEntryEnabled(true); } @Override @@ -81,10 +86,10 @@ public abstract class KeyguardPinBasedInputView extends KeyguardAbsKeyInputView @Override public boolean onKeyDown(int keyCode, KeyEvent event) { if (KeyEvent.isConfirmKey(keyCode)) { - mOkButton.performClick(); + performClick(mOkButton); return true; } else if (keyCode == KeyEvent.KEYCODE_DEL) { - mDeleteButton.performClick(); + performClick(mDeleteButton); return true; } if (keyCode >= KeyEvent.KEYCODE_0 && keyCode <= KeyEvent.KEYCODE_9) { @@ -120,9 +125,42 @@ public abstract class KeyguardPinBasedInputView extends KeyguardAbsKeyInputView } } + private void performClick(View view) { + view.performClick(); + } + private void performNumberClick(int number) { - if (number >= 0 && number <= 9) { - mButtons[number].performClick(); + switch (number) { + case 0: + performClick(mButton0); + break; + case 1: + performClick(mButton1); + break; + case 2: + performClick(mButton2); + break; + case 3: + performClick(mButton3); + break; + case 4: + performClick(mButton4); + break; + case 5: + performClick(mButton5); + break; + case 6: + performClick(mButton6); + break; + case 7: + performClick(mButton7); + break; + case 8: + performClick(mButton8); + break; + case 9: + performClick(mButton9); + break; } } @@ -139,31 +177,94 @@ public abstract class KeyguardPinBasedInputView extends KeyguardAbsKeyInputView @Override protected void onFinishInflate() { mPasswordEntry = findViewById(getPasswordTextViewId()); + mPasswordEntry.setOnKeyListener(this); // Set selected property on so the view can send accessibility events. mPasswordEntry.setSelected(true); + mPasswordEntry.setUserActivityListener(new PasswordTextView.UserActivityListener() { + @Override + public void onUserActivity() { + onUserInput(); + } + }); + mOkButton = findViewById(R.id.key_enter); + if (mOkButton != null) { + mOkButton.setOnTouchListener(this); + mOkButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (mPasswordEntry.isEnabled()) { + verifyPasswordAndUnlock(); + } + } + }); + mOkButton.setOnHoverListener(new LiftToActivateListener(getContext())); + } mDeleteButton = findViewById(R.id.delete_button); mDeleteButton.setVisibility(View.VISIBLE); + mDeleteButton.setOnTouchListener(this); + mDeleteButton.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + // check for time-based lockouts + if (mPasswordEntry.isEnabled()) { + mPasswordEntry.deleteLastChar(); + } + } + }); + mDeleteButton.setOnLongClickListener(new View.OnLongClickListener() { + @Override + public boolean onLongClick(View v) { + // check for time-based lockouts + if (mPasswordEntry.isEnabled()) { + resetPasswordText(true /* animate */, true /* announce */); + } + doHapticKeyClick(); + return true; + } + }); - mButtons[0] = findViewById(R.id.key0); - mButtons[1] = findViewById(R.id.key1); - mButtons[2] = findViewById(R.id.key2); - mButtons[3] = findViewById(R.id.key3); - mButtons[4] = findViewById(R.id.key4); - mButtons[5] = findViewById(R.id.key5); - mButtons[6] = findViewById(R.id.key6); - mButtons[7] = findViewById(R.id.key7); - mButtons[8] = findViewById(R.id.key8); - mButtons[9] = findViewById(R.id.key9); + mButton0 = findViewById(R.id.key0); + mButton1 = findViewById(R.id.key1); + mButton2 = findViewById(R.id.key2); + mButton3 = findViewById(R.id.key3); + mButton4 = findViewById(R.id.key4); + mButton5 = findViewById(R.id.key5); + mButton6 = findViewById(R.id.key6); + mButton7 = findViewById(R.id.key7); + mButton8 = findViewById(R.id.key8); + mButton9 = findViewById(R.id.key9); mPasswordEntry.requestFocus(); super.onFinishInflate(); } @Override + public void onResume(int reason) { + super.onResume(reason); + mPasswordEntry.requestFocus(); + } + + @Override + public boolean onTouch(View v, MotionEvent event) { + if (event.getActionMasked() == MotionEvent.ACTION_DOWN) { + doHapticKeyClick(); + } + return false; + } + + @Override + public boolean onKey(View v, int keyCode, KeyEvent event) { + if (event.getAction() == KeyEvent.ACTION_DOWN) { + return onKeyDown(keyCode, event); + } + return false; + } + + @Override public CharSequence getTitle() { return getContext().getString( com.android.internal.R.string.keyguard_accessibility_pin_unlock); diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java deleted file mode 100644 index 4d0ebfffbe04..000000000000 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java +++ /dev/null @@ -1,113 +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.keyguard; - -import android.view.KeyEvent; -import android.view.MotionEvent; -import android.view.View; -import android.view.View.OnKeyListener; -import android.view.View.OnTouchListener; - -import com.android.internal.util.LatencyTracker; -import com.android.internal.widget.LockPatternUtils; -import com.android.keyguard.KeyguardSecurityModel.SecurityMode; -import com.android.systemui.R; - -public abstract class KeyguardPinBasedInputViewController<T extends KeyguardPinBasedInputView> - extends KeyguardAbsKeyInputViewController<T> { - - private final LiftToActivateListener mLiftToActivateListener; - protected PasswordTextView mPasswordEntry; - - private final OnKeyListener mOnKeyListener = (v, keyCode, event) -> { - if (event.getAction() == KeyEvent.ACTION_DOWN) { - return mView.onKeyDown(keyCode, event); - } - return false; - }; - - private final OnTouchListener mOnTouchListener = (v, event) -> { - if (event.getActionMasked() == MotionEvent.ACTION_DOWN) { - mView.doHapticKeyClick(); - } - return false; - }; - - protected KeyguardPinBasedInputViewController(T view, - KeyguardUpdateMonitor keyguardUpdateMonitor, - SecurityMode securityMode, - LockPatternUtils lockPatternUtils, - KeyguardSecurityCallback keyguardSecurityCallback, - KeyguardMessageAreaController.Factory messageAreaControllerFactory, - LatencyTracker latencyTracker, - LiftToActivateListener liftToActivateListener) { - super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback, - messageAreaControllerFactory, latencyTracker); - mLiftToActivateListener = liftToActivateListener; - mPasswordEntry = mView.findViewById(mView.getPasswordTextViewId()); - } - - @Override - protected void onViewAttached() { - super.onViewAttached(); - - mPasswordEntry.setOnKeyListener(mOnKeyListener); - mPasswordEntry.setUserActivityListener(this::onUserInput); - - View deleteButton = mView.findViewById(R.id.delete_button); - deleteButton.setOnTouchListener(mOnTouchListener); - deleteButton.setOnClickListener(v -> { - // check for time-based lockouts - if (mPasswordEntry.isEnabled()) { - mPasswordEntry.deleteLastChar(); - } - }); - deleteButton.setOnLongClickListener(v -> { - // check for time-based lockouts - if (mPasswordEntry.isEnabled()) { - mView.resetPasswordText(true /* animate */, true /* announce */); - } - mView.doHapticKeyClick(); - return true; - }); - - View okButton = mView.findViewById(R.id.key_enter); - if (okButton != null) { - okButton.setOnTouchListener(mOnTouchListener); - okButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - if (mPasswordEntry.isEnabled()) { - verifyPasswordAndUnlock(); - } - } - }); - okButton.setOnHoverListener(mLiftToActivateListener); - } - } - - @Override - public void onResume(int reason) { - super.onResume(reason); - mPasswordEntry.requestFocus(); - } - - @Override - void resetState() { - mView.setPasswordEntryEnabled(true); - } -} diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java deleted file mode 100644 index 6769436be8ef..000000000000 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java +++ /dev/null @@ -1,66 +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.keyguard; - -import android.view.View; - -import com.android.internal.util.LatencyTracker; -import com.android.internal.widget.LockPatternUtils; -import com.android.keyguard.KeyguardSecurityModel.SecurityMode; -import com.android.systemui.R; - -public class KeyguardPinViewController - extends KeyguardPinBasedInputViewController<KeyguardPINView> { - private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; - - protected KeyguardPinViewController(KeyguardPINView view, - KeyguardUpdateMonitor keyguardUpdateMonitor, - SecurityMode securityMode, LockPatternUtils lockPatternUtils, - KeyguardSecurityCallback keyguardSecurityCallback, - KeyguardMessageAreaController.Factory messageAreaControllerFactory, - LatencyTracker latencyTracker, - LiftToActivateListener liftToActivateListener) { - super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback, - messageAreaControllerFactory, latencyTracker, liftToActivateListener); - mKeyguardUpdateMonitor = keyguardUpdateMonitor; - } - - @Override - protected void onViewAttached() { - super.onViewAttached(); - - View cancelBtn = mView.findViewById(R.id.cancel_button); - if (cancelBtn != null) { - cancelBtn.setOnClickListener(view -> { - getKeyguardSecurityCallback().reset(); - getKeyguardSecurityCallback().onCancelClicked(); - }); - } - } - - @Override - void resetState() { - super.resetState(); - mMessageAreaController.setMessage(""); - } - - @Override - public boolean startDisappearAnimation(Runnable finishRunnable) { - return mView.startDisappearAnimation( - mKeyguardUpdateMonitor.needsSlowUnlockTransition(), finishRunnable); - } -} diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java index b62ea6bc2ff6..81d37a830f8f 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java @@ -19,6 +19,8 @@ import static android.view.WindowInsets.Type.ime; import static android.view.WindowInsets.Type.systemBars; import static android.view.WindowInsetsAnimation.Callback.DISPATCH_MODE_STOP; +import static com.android.systemui.DejankUtils.whitelistIpcs; + import static java.lang.Integer.max; import android.animation.Animator; @@ -26,14 +28,25 @@ import android.animation.AnimatorListenerAdapter; import android.animation.ValueAnimator; import android.app.Activity; import android.app.AlertDialog; +import android.app.admin.DevicePolicyManager; import android.content.Context; +import android.content.Intent; +import android.content.res.ColorStateList; import android.graphics.Insets; import android.graphics.Rect; +import android.metrics.LogMaker; +import android.os.Handler; +import android.os.Looper; +import android.os.UserHandle; import android.util.AttributeSet; +import android.util.Log; import android.util.MathUtils; +import android.util.Slog; import android.util.TypedValue; +import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.VelocityTracker; +import android.view.View; import android.view.ViewConfiguration; import android.view.WindowInsets; import android.view.WindowInsetsAnimation; @@ -48,30 +61,42 @@ import androidx.annotation.VisibleForTesting; import androidx.dynamicanimation.animation.DynamicAnimation; import androidx.dynamicanimation.animation.SpringAnimation; +import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.UiEvent; import com.android.internal.logging.UiEventLogger; +import com.android.internal.logging.UiEventLoggerImpl; +import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.widget.LockPatternUtils; import com.android.keyguard.KeyguardSecurityModel.SecurityMode; +import com.android.settingslib.utils.ThreadUtils; +import com.android.systemui.Dependency; import com.android.systemui.Interpolators; import com.android.systemui.R; +import com.android.systemui.SystemUIFactory; +import com.android.systemui.shared.system.SysUiStatsLog; +import com.android.systemui.statusbar.policy.KeyguardStateController; +import com.android.systemui.util.InjectionInflationController; import java.util.List; -public class KeyguardSecurityContainer extends FrameLayout { - static final int USER_TYPE_PRIMARY = 1; - static final int USER_TYPE_WORK_PROFILE = 2; - static final int USER_TYPE_SECONDARY_USER = 3; +public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSecurityView { + private static final boolean DEBUG = KeyguardConstants.DEBUG; + private static final String TAG = "KeyguardSecurityView"; + + private static final int USER_TYPE_PRIMARY = 1; + private static final int USER_TYPE_WORK_PROFILE = 2; + private static final int USER_TYPE_SECONDARY_USER = 3; // Bouncer is dismissed due to no security. - static final int BOUNCER_DISMISS_NONE_SECURITY = 0; + private static final int BOUNCER_DISMISS_NONE_SECURITY = 0; // Bouncer is dismissed due to pin, password or pattern entered. - static final int BOUNCER_DISMISS_PASSWORD = 1; + private static final int BOUNCER_DISMISS_PASSWORD = 1; // Bouncer is dismissed due to biometric (face, fingerprint or iris) authenticated. - static final int BOUNCER_DISMISS_BIOMETRIC = 2; + private static final int BOUNCER_DISMISS_BIOMETRIC = 2; // Bouncer is dismissed due to extended access granted. - static final int BOUNCER_DISMISS_EXTENDED_ACCESS = 3; + private static final int BOUNCER_DISMISS_EXTENDED_ACCESS = 3; // Bouncer is dismissed due to sim card unlock code entered. - static final int BOUNCER_DISMISS_SIM = 4; + private static final int BOUNCER_DISMISS_SIM = 4; // Make the view move slower than the finger, as if the spring were applying force. private static final float TOUCH_Y_MULTIPLIER = 0.25f; @@ -80,23 +105,36 @@ public class KeyguardSecurityContainer extends FrameLayout { // How much to scale the default slop by, to avoid accidental drags. private static final float SLOP_SCALE = 4f; + private static final UiEventLogger sUiEventLogger = new UiEventLoggerImpl(); + private static final long IME_DISAPPEAR_DURATION_MS = 125; + private KeyguardSecurityModel mSecurityModel; + private LockPatternUtils mLockPatternUtils; + @VisibleForTesting KeyguardSecurityViewFlipper mSecurityViewFlipper; + private boolean mIsVerifyUnlockOnly; + private SecurityMode mCurrentSecuritySelection = SecurityMode.Invalid; + private KeyguardSecurityView mCurrentSecurityView; + private SecurityCallback mSecurityCallback; private AlertDialog mAlertDialog; + private InjectionInflationController mInjectionInflationController; private boolean mSwipeUpToRetry; + private AdminSecondaryLockScreenController mSecondaryLockScreenController; private final ViewConfiguration mViewConfiguration; private final SpringAnimation mSpringAnimation; private final VelocityTracker mVelocityTracker = VelocityTracker.obtain(); + private final KeyguardUpdateMonitor mUpdateMonitor; + private final KeyguardStateController mKeyguardStateController; + private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class); private float mLastTouchY = -1; private int mActivePointerId = -1; private boolean mIsDragging; private float mStartTouchY = -1; private boolean mDisappearAnimRunning; - private SwipeListener mSwipeListener; private final WindowInsetsAnimation.Callback mWindowInsetsAnimationCallback = new WindowInsetsAnimation.Callback(DISPATCH_MODE_STOP) { @@ -148,22 +186,19 @@ public class KeyguardSecurityContainer extends FrameLayout { // Used to notify the container when something interesting happens. public interface SecurityCallback { - boolean dismiss(boolean authenticated, int targetUserId, boolean bypassSecondaryLockScreen); - void userActivity(); - void onSecurityModeChanged(SecurityMode securityMode, boolean needsInput); + public boolean dismiss(boolean authenticated, int targetUserId, + boolean bypassSecondaryLockScreen); + public void userActivity(); + public void onSecurityModeChanged(SecurityMode securityMode, boolean needsInput); /** * @param strongAuth wheher the user has authenticated with strong authentication like * pattern, password or PIN but not by trust agents or fingerprint * @param targetUserId a user that needs to be the foreground user at the finish completion. */ - void finish(boolean strongAuth, int targetUserId); - void reset(); - void onCancelClicked(); - } - - public interface SwipeListener { - void onSwipeUp(); + public void finish(boolean strongAuth, int targetUserId); + public void reset(); + public void onCancelClicked(); } @VisibleForTesting @@ -214,24 +249,52 @@ public class KeyguardSecurityContainer extends FrameLayout { public KeyguardSecurityContainer(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); + mSecurityModel = Dependency.get(KeyguardSecurityModel.class); + mLockPatternUtils = new LockPatternUtils(context); + mUpdateMonitor = Dependency.get(KeyguardUpdateMonitor.class); mSpringAnimation = new SpringAnimation(this, DynamicAnimation.Y); + mInjectionInflationController = new InjectionInflationController( + SystemUIFactory.getInstance().getSysUIComponent().createViewInstanceCreatorFactory()); mViewConfiguration = ViewConfiguration.get(context); + mKeyguardStateController = Dependency.get(KeyguardStateController.class); + mSecondaryLockScreenController = new AdminSecondaryLockScreenController(context, this, + mUpdateMonitor, mCallback, new Handler(Looper.myLooper())); + } + + public void setSecurityCallback(SecurityCallback callback) { + mSecurityCallback = callback; } - void onResume(SecurityMode securityMode, boolean faceAuthEnabled) { + @Override + public void onResume(int reason) { + if (mCurrentSecuritySelection != SecurityMode.None) { + getSecurityView(mCurrentSecuritySelection).onResume(reason); + } mSecurityViewFlipper.setWindowInsetsAnimationCallback(mWindowInsetsAnimationCallback); - updateBiometricRetry(securityMode, faceAuthEnabled); + updateBiometricRetry(); } + @Override public void onPause() { if (mAlertDialog != null) { mAlertDialog.dismiss(); mAlertDialog = null; } + mSecondaryLockScreenController.hide(); + if (mCurrentSecuritySelection != SecurityMode.None) { + getSecurityView(mCurrentSecuritySelection).onPause(); + } mSecurityViewFlipper.setWindowInsetsAnimationCallback(null); } @Override + public void onStartingToHide() { + if (mCurrentSecuritySelection != SecurityMode.None) { + getSecurityView(mCurrentSecuritySelection).onStartingToHide(); + } + } + + @Override public boolean shouldDelayChildPressedState() { return true; } @@ -253,12 +316,13 @@ public class KeyguardSecurityContainer extends FrameLayout { return false; } // Avoid dragging the pattern view - if (mSecurityViewFlipper.getSecurityView().disallowInterceptTouch(event)) { + if (mCurrentSecurityView.disallowInterceptTouch(event)) { return false; } int index = event.findPointerIndex(mActivePointerId); float touchSlop = mViewConfiguration.getScaledTouchSlop() * SLOP_SCALE; - if (index != -1 && mStartTouchY - event.getY(index) > touchSlop) { + if (mCurrentSecurityView != null && index != -1 + && mStartTouchY - event.getY(index) > touchSlop) { mIsDragging = true; return true; } @@ -306,28 +370,31 @@ public class KeyguardSecurityContainer extends FrameLayout { } if (action == MotionEvent.ACTION_UP) { if (-getTranslationY() > TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, - MIN_DRAG_SIZE, getResources().getDisplayMetrics())) { - if (mSwipeListener != null) { - mSwipeListener.onSwipeUp(); - } + MIN_DRAG_SIZE, getResources().getDisplayMetrics()) + && !mUpdateMonitor.isFaceDetectionRunning()) { + mUpdateMonitor.requestFaceAuth(); + mCallback.userActivity(); + showMessage(null, null); } } return true; } - void setSwipeListener(SwipeListener swipeListener) { - mSwipeListener = swipeListener; - } - private void startSpringAnimation(float startVelocity) { mSpringAnimation .setStartVelocity(startVelocity) .animateToFinalPosition(0); } - public void startDisappearAnimation(SecurityMode securitySelection) { + public void startAppearAnimation() { + if (mCurrentSecuritySelection != SecurityMode.None) { + getSecurityView(mCurrentSecuritySelection).startAppearAnimation(); + } + } + + public boolean startDisappearAnimation(Runnable onFinishRunnable) { mDisappearAnimRunning = true; - if (securitySelection == SecurityMode.Password) { + if (mCurrentSecuritySelection == SecurityMode.Password) { mSecurityViewFlipper.getWindowInsetsController().controlWindowInsetsAnimation(ime(), IME_DISAPPEAR_DURATION_MS, Interpolators.LINEAR, null, new WindowInsetsAnimationControlListener() { @@ -372,13 +439,19 @@ public class KeyguardSecurityContainer extends FrameLayout { } }); } + if (mCurrentSecuritySelection != SecurityMode.None) { + return getSecurityView(mCurrentSecuritySelection).startDisappearAnimation( + onFinishRunnable); + } + return false; } /** * Enables/disables swipe up to retry on the bouncer. */ - private void updateBiometricRetry(SecurityMode securityMode, boolean faceAuthEnabled) { - mSwipeUpToRetry = faceAuthEnabled + private void updateBiometricRetry() { + SecurityMode securityMode = getSecurityMode(); + mSwipeUpToRetry = mKeyguardStateController.isFaceAuthEnabled() && securityMode != SecurityMode.SimPin && securityMode != SecurityMode.SimPuk && securityMode != SecurityMode.None; @@ -388,11 +461,53 @@ public class KeyguardSecurityContainer extends FrameLayout { return mSecurityViewFlipper.getTitle(); } + @VisibleForTesting + protected KeyguardSecurityView getSecurityView(SecurityMode securityMode) { + final int securityViewIdForMode = getSecurityViewIdForMode(securityMode); + KeyguardSecurityView view = null; + final int children = mSecurityViewFlipper.getChildCount(); + for (int child = 0; child < children; child++) { + if (mSecurityViewFlipper.getChildAt(child).getId() == securityViewIdForMode) { + view = ((KeyguardSecurityView)mSecurityViewFlipper.getChildAt(child)); + break; + } + } + int layoutId = getLayoutIdFor(securityMode); + if (view == null && layoutId != 0) { + final LayoutInflater inflater = LayoutInflater.from(mContext); + if (DEBUG) Log.v(TAG, "inflating id = " + layoutId); + View v = mInjectionInflationController.injectable(inflater) + .inflate(layoutId, mSecurityViewFlipper, false); + mSecurityViewFlipper.addView(v); + updateSecurityView(v); + view = (KeyguardSecurityView)v; + view.reset(); + } + + return view; + } + + private void updateSecurityView(View view) { + if (view instanceof KeyguardSecurityView) { + KeyguardSecurityView ksv = (KeyguardSecurityView) view; + ksv.setKeyguardCallback(mCallback); + ksv.setLockPatternUtils(mLockPatternUtils); + } else { + Log.w(TAG, "View " + view + " is not a KeyguardSecurityView"); + } + } @Override public void onFinishInflate() { super.onFinishInflate(); mSecurityViewFlipper = findViewById(R.id.view_flipper); + mSecurityViewFlipper.setLockPatternUtils(mLockPatternUtils); + } + + public void setLockPatternUtils(LockPatternUtils utils) { + mLockPatternUtils = utils; + mSecurityModel.setLockPatternUtils(utils); + mSecurityViewFlipper.setLockPatternUtils(mLockPatternUtils); } @Override @@ -424,12 +539,11 @@ public class KeyguardSecurityContainer extends FrameLayout { mAlertDialog.show(); } - void showTimeoutDialog(int userId, int timeoutMs, LockPatternUtils lockPatternUtils, - SecurityMode securityMode) { - int timeoutInSeconds = timeoutMs / 1000; + private void showTimeoutDialog(int userId, int timeoutMs) { + int timeoutInSeconds = (int) timeoutMs / 1000; int messageId = 0; - switch (securityMode) { + switch (mSecurityModel.getSecurityMode(userId)) { case Pattern: messageId = R.string.kg_too_many_failed_pattern_attempts_dialog_message; break; @@ -449,13 +563,13 @@ public class KeyguardSecurityContainer extends FrameLayout { if (messageId != 0) { final String message = mContext.getString(messageId, - lockPatternUtils.getCurrentFailedPasswordAttempts(userId), + mLockPatternUtils.getCurrentFailedPasswordAttempts(userId), timeoutInSeconds); showDialog(null, message); } } - void showAlmostAtWipeDialog(int attempts, int remaining, int userType) { + private void showAlmostAtWipeDialog(int attempts, int remaining, int userType) { String message = null; switch (userType) { case USER_TYPE_PRIMARY: @@ -474,7 +588,7 @@ public class KeyguardSecurityContainer extends FrameLayout { showDialog(null, message); } - void showWipeDialog(int attempts, int userType) { + private void showWipeDialog(int attempts, int userType) { String message = null; switch (userType) { case USER_TYPE_PRIMARY: @@ -493,8 +607,358 @@ public class KeyguardSecurityContainer extends FrameLayout { showDialog(null, message); } + private void reportFailedUnlockAttempt(int userId, int timeoutMs) { + // +1 for this time + final int failedAttempts = mLockPatternUtils.getCurrentFailedPasswordAttempts(userId) + 1; + + if (DEBUG) Log.d(TAG, "reportFailedPatternAttempt: #" + failedAttempts); + + final DevicePolicyManager dpm = mLockPatternUtils.getDevicePolicyManager(); + final int failedAttemptsBeforeWipe = + dpm.getMaximumFailedPasswordsForWipe(null, userId); + + final int remainingBeforeWipe = failedAttemptsBeforeWipe > 0 ? + (failedAttemptsBeforeWipe - failedAttempts) + : Integer.MAX_VALUE; // because DPM returns 0 if no restriction + if (remainingBeforeWipe < LockPatternUtils.FAILED_ATTEMPTS_BEFORE_WIPE_GRACE) { + // The user has installed a DevicePolicyManager that requests a user/profile to be wiped + // N attempts. Once we get below the grace period, we post this dialog every time as a + // clear warning until the deletion fires. + // Check which profile has the strictest policy for failed password attempts + final int expiringUser = dpm.getProfileWithMinimumFailedPasswordsForWipe(userId); + int userType = USER_TYPE_PRIMARY; + if (expiringUser == userId) { + // TODO: http://b/23522538 + if (expiringUser != UserHandle.USER_SYSTEM) { + userType = USER_TYPE_SECONDARY_USER; + } + } else if (expiringUser != UserHandle.USER_NULL) { + userType = USER_TYPE_WORK_PROFILE; + } // If USER_NULL, which shouldn't happen, leave it as USER_TYPE_PRIMARY + if (remainingBeforeWipe > 0) { + showAlmostAtWipeDialog(failedAttempts, remainingBeforeWipe, userType); + } else { + // Too many attempts. The device will be wiped shortly. + Slog.i(TAG, "Too many unlock attempts; user " + expiringUser + " will be wiped!"); + showWipeDialog(failedAttempts, userType); + } + } + mLockPatternUtils.reportFailedPasswordAttempt(userId); + if (timeoutMs > 0) { + mLockPatternUtils.reportPasswordLockout(timeoutMs, userId); + showTimeoutDialog(userId, timeoutMs); + } + } + + /** + * Shows the primary security screen for the user. This will be either the multi-selector + * or the user's security method. + * @param turningOff true if the device is being turned off + */ + void showPrimarySecurityScreen(boolean turningOff) { + SecurityMode securityMode = whitelistIpcs(() -> mSecurityModel.getSecurityMode( + KeyguardUpdateMonitor.getCurrentUser())); + if (DEBUG) Log.v(TAG, "showPrimarySecurityScreen(turningOff=" + turningOff + ")"); + showSecurityScreen(securityMode); + } + + /** + * Shows the next security screen if there is one. + * @param authenticated true if the user entered the correct authentication + * @param targetUserId a user that needs to be the foreground user at the finish (if called) + * completion. + * @param bypassSecondaryLockScreen true if the user is allowed to bypass the secondary + * secondary lock screen requirement, if any. + * @return true if keyguard is done + */ + boolean showNextSecurityScreenOrFinish(boolean authenticated, int targetUserId, + boolean bypassSecondaryLockScreen) { + if (DEBUG) Log.d(TAG, "showNextSecurityScreenOrFinish(" + authenticated + ")"); + boolean finish = false; + boolean strongAuth = false; + int eventSubtype = -1; + BouncerUiEvent uiEvent = BouncerUiEvent.UNKNOWN; + if (mUpdateMonitor.getUserHasTrust(targetUserId)) { + finish = true; + eventSubtype = BOUNCER_DISMISS_EXTENDED_ACCESS; + uiEvent = BouncerUiEvent.BOUNCER_DISMISS_EXTENDED_ACCESS; + } else if (mUpdateMonitor.getUserUnlockedWithBiometric(targetUserId)) { + finish = true; + eventSubtype = BOUNCER_DISMISS_BIOMETRIC; + uiEvent = BouncerUiEvent.BOUNCER_DISMISS_BIOMETRIC; + } else if (SecurityMode.None == mCurrentSecuritySelection) { + SecurityMode securityMode = mSecurityModel.getSecurityMode(targetUserId); + if (SecurityMode.None == securityMode) { + finish = true; // no security required + eventSubtype = BOUNCER_DISMISS_NONE_SECURITY; + uiEvent = BouncerUiEvent.BOUNCER_DISMISS_NONE_SECURITY; + } else { + showSecurityScreen(securityMode); // switch to the alternate security view + } + } else if (authenticated) { + switch (mCurrentSecuritySelection) { + case Pattern: + case Password: + case PIN: + strongAuth = true; + finish = true; + eventSubtype = BOUNCER_DISMISS_PASSWORD; + uiEvent = BouncerUiEvent.BOUNCER_DISMISS_PASSWORD; + break; + + case SimPin: + case SimPuk: + // Shortcut for SIM PIN/PUK to go to directly to user's security screen or home + SecurityMode securityMode = mSecurityModel.getSecurityMode(targetUserId); + if (securityMode == SecurityMode.None && mLockPatternUtils.isLockScreenDisabled( + KeyguardUpdateMonitor.getCurrentUser())) { + finish = true; + eventSubtype = BOUNCER_DISMISS_SIM; + uiEvent = BouncerUiEvent.BOUNCER_DISMISS_SIM; + } else { + showSecurityScreen(securityMode); + } + break; + + default: + Log.v(TAG, "Bad security screen " + mCurrentSecuritySelection + ", fail safe"); + showPrimarySecurityScreen(false); + break; + } + } + // Check for device admin specified additional security measures. + if (finish && !bypassSecondaryLockScreen) { + Intent secondaryLockscreenIntent = + mUpdateMonitor.getSecondaryLockscreenRequirement(targetUserId); + if (secondaryLockscreenIntent != null) { + mSecondaryLockScreenController.show(secondaryLockscreenIntent); + return false; + } + } + if (eventSubtype != -1) { + mMetricsLogger.write(new LogMaker(MetricsEvent.BOUNCER) + .setType(MetricsEvent.TYPE_DISMISS).setSubtype(eventSubtype)); + } + if (uiEvent != BouncerUiEvent.UNKNOWN) { + sUiEventLogger.log(uiEvent); + } + if (finish) { + mSecurityCallback.finish(strongAuth, targetUserId); + } + return finish; + } + + /** + * Switches to the given security view unless it's already being shown, in which case + * this is a no-op. + * + * @param securityMode + */ + private void showSecurityScreen(SecurityMode securityMode) { + if (DEBUG) Log.d(TAG, "showSecurityScreen(" + securityMode + ")"); + + if (securityMode == mCurrentSecuritySelection) return; + + KeyguardSecurityView oldView = getSecurityView(mCurrentSecuritySelection); + KeyguardSecurityView newView = getSecurityView(securityMode); + + // Emulate Activity life cycle + if (oldView != null) { + oldView.onPause(); + oldView.setKeyguardCallback(mNullCallback); // ignore requests from old view + } + if (securityMode != SecurityMode.None) { + newView.onResume(KeyguardSecurityView.VIEW_REVEALED); + newView.setKeyguardCallback(mCallback); + } + + // Find and show this child. + final int childCount = mSecurityViewFlipper.getChildCount(); + + final int securityViewIdForMode = getSecurityViewIdForMode(securityMode); + for (int i = 0; i < childCount; i++) { + if (mSecurityViewFlipper.getChildAt(i).getId() == securityViewIdForMode) { + mSecurityViewFlipper.setDisplayedChild(i); + break; + } + } + + mCurrentSecuritySelection = securityMode; + mCurrentSecurityView = newView; + mSecurityCallback.onSecurityModeChanged(securityMode, + securityMode != SecurityMode.None && newView.needsInput()); + } + + private KeyguardSecurityCallback mCallback = new KeyguardSecurityCallback() { + public void userActivity() { + if (mSecurityCallback != null) { + mSecurityCallback.userActivity(); + } + } + + @Override + public void onUserInput() { + mUpdateMonitor.cancelFaceAuth(); + } + + @Override + public void dismiss(boolean authenticated, int targetId) { + dismiss(authenticated, targetId, /* bypassSecondaryLockScreen */ false); + } + + @Override + public void dismiss(boolean authenticated, int targetId, + boolean bypassSecondaryLockScreen) { + mSecurityCallback.dismiss(authenticated, targetId, bypassSecondaryLockScreen); + } + + public boolean isVerifyUnlockOnly() { + return mIsVerifyUnlockOnly; + } + + public void reportUnlockAttempt(int userId, boolean success, int timeoutMs) { + if (success) { + SysUiStatsLog.write(SysUiStatsLog.KEYGUARD_BOUNCER_PASSWORD_ENTERED, + SysUiStatsLog.KEYGUARD_BOUNCER_PASSWORD_ENTERED__RESULT__SUCCESS); + mLockPatternUtils.reportSuccessfulPasswordAttempt(userId); + // Force a garbage collection in an attempt to erase any lockscreen password left in + // memory. Do it asynchronously with a 5-sec delay to avoid making the keyguard + // dismiss animation janky. + ThreadUtils.postOnBackgroundThread(() -> { + try { + Thread.sleep(5000); + } catch (InterruptedException ignored) { } + Runtime.getRuntime().gc(); + }); + } else { + SysUiStatsLog.write(SysUiStatsLog.KEYGUARD_BOUNCER_PASSWORD_ENTERED, + SysUiStatsLog.KEYGUARD_BOUNCER_PASSWORD_ENTERED__RESULT__FAILURE); + KeyguardSecurityContainer.this.reportFailedUnlockAttempt(userId, timeoutMs); + } + mMetricsLogger.write(new LogMaker(MetricsEvent.BOUNCER) + .setType(success ? MetricsEvent.TYPE_SUCCESS : MetricsEvent.TYPE_FAILURE)); + sUiEventLogger.log(success ? BouncerUiEvent.BOUNCER_PASSWORD_SUCCESS + : BouncerUiEvent.BOUNCER_PASSWORD_FAILURE); + } + + public void reset() { + mSecurityCallback.reset(); + } + + public void onCancelClicked() { + mSecurityCallback.onCancelClicked(); + } + }; + + // The following is used to ignore callbacks from SecurityViews that are no longer current + // (e.g. face unlock). This avoids unwanted asynchronous events from messing with the + // state for the current security method. + private KeyguardSecurityCallback mNullCallback = new KeyguardSecurityCallback() { + @Override + public void userActivity() { } + @Override + public void reportUnlockAttempt(int userId, boolean success, int timeoutMs) { } + @Override + public boolean isVerifyUnlockOnly() { return false; } + @Override + public void dismiss(boolean securityVerified, int targetUserId) { } + @Override + public void dismiss(boolean authenticated, int targetId, + boolean bypassSecondaryLockScreen) { } + @Override + public void onUserInput() { } + @Override + public void reset() {} + }; + + private int getSecurityViewIdForMode(SecurityMode securityMode) { + switch (securityMode) { + case Pattern: return R.id.keyguard_pattern_view; + case PIN: return R.id.keyguard_pin_view; + case Password: return R.id.keyguard_password_view; + case SimPin: return R.id.keyguard_sim_pin_view; + case SimPuk: return R.id.keyguard_sim_puk_view; + } + return 0; + } + + @VisibleForTesting + public int getLayoutIdFor(SecurityMode securityMode) { + switch (securityMode) { + case Pattern: return R.layout.keyguard_pattern_view; + case PIN: return R.layout.keyguard_pin_view; + case Password: return R.layout.keyguard_password_view; + case SimPin: return R.layout.keyguard_sim_pin_view; + case SimPuk: return R.layout.keyguard_sim_puk_view; + default: + return 0; + } + } + + public SecurityMode getSecurityMode() { + return mSecurityModel.getSecurityMode(KeyguardUpdateMonitor.getCurrentUser()); + } + + public SecurityMode getCurrentSecurityMode() { + return mCurrentSecuritySelection; + } + + public KeyguardSecurityView getCurrentSecurityView() { + return mCurrentSecurityView; + } + + public void verifyUnlock() { + mIsVerifyUnlockOnly = true; + showSecurityScreen(getSecurityMode()); + } + + public SecurityMode getCurrentSecuritySelection() { + return mCurrentSecuritySelection; + } + + public void dismiss(boolean authenticated, int targetUserId) { + mCallback.dismiss(authenticated, targetUserId); + } + + public boolean needsInput() { + return mSecurityViewFlipper.needsInput(); + } + + @Override + public void setKeyguardCallback(KeyguardSecurityCallback callback) { + mSecurityViewFlipper.setKeyguardCallback(callback); + } + + @Override public void reset() { + mSecurityViewFlipper.reset(); mDisappearAnimRunning = false; } + + @Override + public KeyguardSecurityCallback getCallback() { + return mSecurityViewFlipper.getCallback(); + } + + @Override + public void showPromptReason(int reason) { + if (mCurrentSecuritySelection != SecurityMode.None) { + if (reason != PROMPT_REASON_NONE) { + Log.i(TAG, "Strong auth required, reason: " + reason); + } + getSecurityView(mCurrentSecuritySelection).showPromptReason(reason); + } + } + + public void showMessage(CharSequence message, ColorStateList colorState) { + if (mCurrentSecuritySelection != SecurityMode.None) { + getSecurityView(mCurrentSecuritySelection).showMessage(message, colorState); + } + } + + @Override + public void showUsabilityHint() { + mSecurityViewFlipper.showUsabilityHint(); + } } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java index 64676e55b038..17f25bd08ef4 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java @@ -16,166 +16,33 @@ package com.android.keyguard; -import static com.android.keyguard.KeyguardSecurityContainer.BOUNCER_DISMISS_BIOMETRIC; -import static com.android.keyguard.KeyguardSecurityContainer.BOUNCER_DISMISS_EXTENDED_ACCESS; -import static com.android.keyguard.KeyguardSecurityContainer.BOUNCER_DISMISS_NONE_SECURITY; -import static com.android.keyguard.KeyguardSecurityContainer.BOUNCER_DISMISS_PASSWORD; -import static com.android.keyguard.KeyguardSecurityContainer.BOUNCER_DISMISS_SIM; -import static com.android.keyguard.KeyguardSecurityContainer.USER_TYPE_PRIMARY; -import static com.android.keyguard.KeyguardSecurityContainer.USER_TYPE_SECONDARY_USER; -import static com.android.keyguard.KeyguardSecurityContainer.USER_TYPE_WORK_PROFILE; -import static com.android.systemui.DejankUtils.whitelistIpcs; - -import android.app.admin.DevicePolicyManager; -import android.content.Intent; import android.content.res.ColorStateList; -import android.metrics.LogMaker; -import android.os.UserHandle; -import android.util.Log; -import android.util.Slog; -import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.logging.MetricsLogger; -import com.android.internal.logging.UiEventLogger; -import com.android.internal.logging.nano.MetricsProto; -import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.widget.LockPatternUtils; -import com.android.keyguard.KeyguardSecurityContainer.BouncerUiEvent; import com.android.keyguard.KeyguardSecurityContainer.SecurityCallback; -import com.android.keyguard.KeyguardSecurityContainer.SwipeListener; import com.android.keyguard.KeyguardSecurityModel.SecurityMode; -import com.android.keyguard.dagger.KeyguardBouncerScope; -import com.android.settingslib.utils.ThreadUtils; -import com.android.systemui.shared.system.SysUiStatsLog; -import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.util.ViewController; import javax.inject.Inject; /** Controller for {@link KeyguardSecurityContainer} */ -@KeyguardBouncerScope -public class KeyguardSecurityContainerController extends ViewController<KeyguardSecurityContainer> - implements KeyguardSecurityView { - - private static final boolean DEBUG = KeyguardConstants.DEBUG; - private static final String TAG = "KeyguardSecurityView"; +public class KeyguardSecurityContainerController extends ViewController<KeyguardSecurityContainer> { - private final AdminSecondaryLockScreenController mAdminSecondaryLockScreenController; private final LockPatternUtils mLockPatternUtils; - private final KeyguardUpdateMonitor mUpdateMonitor; - private final KeyguardSecurityModel mSecurityModel; - private final MetricsLogger mMetricsLogger; - private final UiEventLogger mUiEventLogger; - private final KeyguardStateController mKeyguardStateController; - private final KeyguardSecurityViewFlipperController mSecurityViewFlipperController; - - private SecurityCallback mSecurityCallback; - private SecurityMode mCurrentSecurityMode = SecurityMode.Invalid; - - private KeyguardSecurityCallback mKeyguardSecurityCallback = new KeyguardSecurityCallback() { - public void userActivity() { - if (mSecurityCallback != null) { - mSecurityCallback.userActivity(); - } - } - - @Override - public void onUserInput() { - mUpdateMonitor.cancelFaceAuth(); - } - - @Override - public void dismiss(boolean authenticated, int targetId) { - dismiss(authenticated, targetId, /* bypassSecondaryLockScreen */ false); - } - - @Override - public void dismiss(boolean authenticated, int targetId, - boolean bypassSecondaryLockScreen) { - mSecurityCallback.dismiss(authenticated, targetId, bypassSecondaryLockScreen); - } - - public boolean isVerifyUnlockOnly() { - return false; - } - - public void reportUnlockAttempt(int userId, boolean success, int timeoutMs) { - if (success) { - SysUiStatsLog.write(SysUiStatsLog.KEYGUARD_BOUNCER_PASSWORD_ENTERED, - SysUiStatsLog.KEYGUARD_BOUNCER_PASSWORD_ENTERED__RESULT__SUCCESS); - mLockPatternUtils.reportSuccessfulPasswordAttempt(userId); - // Force a garbage collection in an attempt to erase any lockscreen password left in - // memory. Do it asynchronously with a 5-sec delay to avoid making the keyguard - // dismiss animation janky. - ThreadUtils.postOnBackgroundThread(() -> { - try { - Thread.sleep(5000); - } catch (InterruptedException ignored) { } - Runtime.getRuntime().gc(); - }); - } else { - SysUiStatsLog.write(SysUiStatsLog.KEYGUARD_BOUNCER_PASSWORD_ENTERED, - SysUiStatsLog.KEYGUARD_BOUNCER_PASSWORD_ENTERED__RESULT__FAILURE); - reportFailedUnlockAttempt(userId, timeoutMs); - } - mMetricsLogger.write(new LogMaker(MetricsEvent.BOUNCER) - .setType(success ? MetricsEvent.TYPE_SUCCESS : MetricsEvent.TYPE_FAILURE)); - mUiEventLogger.log(success ? BouncerUiEvent.BOUNCER_PASSWORD_SUCCESS - : BouncerUiEvent.BOUNCER_PASSWORD_FAILURE); - } - - public void reset() { - mSecurityCallback.reset(); - } - - public void onCancelClicked() { - mSecurityCallback.onCancelClicked(); - } - }; - - - private SwipeListener mSwipeListener = new SwipeListener() { - @Override - public void onSwipeUp() { - if (!mUpdateMonitor.isFaceDetectionRunning()) { - mUpdateMonitor.requestFaceAuth(); - mKeyguardSecurityCallback.userActivity(); - showMessage(null, null); - } - } - }; + private final KeyguardSecurityViewController.Factory mKeyguardSecurityViewControllerFactory; @Inject KeyguardSecurityContainerController(KeyguardSecurityContainer view, - AdminSecondaryLockScreenController.Factory adminSecondaryLockScreenControllerFactory, LockPatternUtils lockPatternUtils, - KeyguardUpdateMonitor keyguardUpdateMonitor, - KeyguardSecurityModel keyguardSecurityModel, - MetricsLogger metricsLogger, - UiEventLogger uiEventLogger, - KeyguardStateController keyguardStateController, - KeyguardSecurityViewFlipperController securityViewFlipperController) { + KeyguardSecurityViewController.Factory keyguardSecurityViewControllerFactory) { super(view); mLockPatternUtils = lockPatternUtils; - mUpdateMonitor = keyguardUpdateMonitor; - mSecurityModel = keyguardSecurityModel; - mMetricsLogger = metricsLogger; - mUiEventLogger = uiEventLogger; - mKeyguardStateController = keyguardStateController; - mSecurityViewFlipperController = securityViewFlipperController; - mAdminSecondaryLockScreenController = adminSecondaryLockScreenControllerFactory.create( - mKeyguardSecurityCallback); - } - - @Override - public void init() { - super.init(); - mSecurityViewFlipperController.init(); + view.setLockPatternUtils(mLockPatternUtils); + mKeyguardSecurityViewControllerFactory = keyguardSecurityViewControllerFactory; } @Override protected void onViewAttached() { - mView.setSwipeListener(mSwipeListener); } @Override @@ -184,270 +51,68 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard /** */ public void onPause() { - mAdminSecondaryLockScreenController.hide(); - if (mCurrentSecurityMode != SecurityMode.None) { - getCurrentSecurityController().onPause(); - } mView.onPause(); } - - /** - * Shows the primary security screen for the user. This will be either the multi-selector - * or the user's security method. - * @param turningOff true if the device is being turned off - */ public void showPrimarySecurityScreen(boolean turningOff) { - SecurityMode securityMode = whitelistIpcs(() -> mSecurityModel.getSecurityMode( - KeyguardUpdateMonitor.getCurrentUser())); - if (DEBUG) Log.v(TAG, "showPrimarySecurityScreen(turningOff=" + turningOff + ")"); - showSecurityScreen(securityMode); + mView.showPrimarySecurityScreen(turningOff); } - @Override public void showPromptReason(int reason) { - if (mCurrentSecurityMode != SecurityMode.None) { - if (reason != PROMPT_REASON_NONE) { - Log.i(TAG, "Strong auth required, reason: " + reason); - } - getCurrentSecurityController().showPromptReason(reason); - } + mView.showPromptReason(reason); } public void showMessage(CharSequence message, ColorStateList colorState) { - if (mCurrentSecurityMode != SecurityMode.None) { - getCurrentSecurityController().showMessage(message, colorState); - } + mView.showMessage(message, colorState); } - public SecurityMode getCurrentSecurityMode() { - return mCurrentSecurityMode; + public SecurityMode getCurrentSecuritySelection() { + return mView.getCurrentSecuritySelection(); } public void dismiss(boolean authenticated, int targetUserId) { - mKeyguardSecurityCallback.dismiss(authenticated, targetUserId); + mView.dismiss(authenticated, targetUserId); } public void reset() { mView.reset(); - mSecurityViewFlipperController.reset(); } public CharSequence getTitle() { return mView.getTitle(); } - @Override - public void onResume(int reason) { - if (mCurrentSecurityMode != SecurityMode.None) { - getCurrentSecurityController().onResume(reason); - } - mView.onResume( - mSecurityModel.getSecurityMode(KeyguardUpdateMonitor.getCurrentUser()), - mKeyguardStateController.isFaceAuthEnabled()); + public void onResume(int screenOn) { + mView.onResume(screenOn); } public void startAppearAnimation() { - if (mCurrentSecurityMode != SecurityMode.None) { - getCurrentSecurityController().startAppearAnimation(); - } + mView.startAppearAnimation(); } public boolean startDisappearAnimation(Runnable onFinishRunnable) { - mView.startDisappearAnimation(getCurrentSecurityMode()); - - if (mCurrentSecurityMode != SecurityMode.None) { - return getCurrentSecurityController().startDisappearAnimation(onFinishRunnable); - } - - return false; + return mView.startDisappearAnimation(onFinishRunnable); } public void onStartingToHide() { - if (mCurrentSecurityMode != SecurityMode.None) { - getCurrentSecurityController().onStartingToHide(); - } + mView.onStartingToHide(); } public void setSecurityCallback(SecurityCallback securityCallback) { - mSecurityCallback = securityCallback; + mView.setSecurityCallback(securityCallback); } - /** - * Shows the next security screen if there is one. - * @param authenticated true if the user entered the correct authentication - * @param targetUserId a user that needs to be the foreground user at the finish (if called) - * completion. - * @param bypassSecondaryLockScreen true if the user is allowed to bypass the secondary - * secondary lock screen requirement, if any. - * @return true if keyguard is done - */ public boolean showNextSecurityScreenOrFinish(boolean authenticated, int targetUserId, boolean bypassSecondaryLockScreen) { - - if (DEBUG) Log.d(TAG, "showNextSecurityScreenOrFinish(" + authenticated + ")"); - boolean finish = false; - boolean strongAuth = false; - int eventSubtype = -1; - BouncerUiEvent uiEvent = BouncerUiEvent.UNKNOWN; - if (mUpdateMonitor.getUserHasTrust(targetUserId)) { - finish = true; - eventSubtype = BOUNCER_DISMISS_EXTENDED_ACCESS; - uiEvent = BouncerUiEvent.BOUNCER_DISMISS_EXTENDED_ACCESS; - } else if (mUpdateMonitor.getUserUnlockedWithBiometric(targetUserId)) { - finish = true; - eventSubtype = BOUNCER_DISMISS_BIOMETRIC; - uiEvent = BouncerUiEvent.BOUNCER_DISMISS_BIOMETRIC; - } else if (SecurityMode.None == getCurrentSecurityMode()) { - SecurityMode securityMode = mSecurityModel.getSecurityMode(targetUserId); - if (SecurityMode.None == securityMode) { - finish = true; // no security required - eventSubtype = BOUNCER_DISMISS_NONE_SECURITY; - uiEvent = BouncerUiEvent.BOUNCER_DISMISS_NONE_SECURITY; - } else { - showSecurityScreen(securityMode); // switch to the alternate security view - } - } else if (authenticated) { - switch (getCurrentSecurityMode()) { - case Pattern: - case Password: - case PIN: - strongAuth = true; - finish = true; - eventSubtype = BOUNCER_DISMISS_PASSWORD; - uiEvent = BouncerUiEvent.BOUNCER_DISMISS_PASSWORD; - break; - - case SimPin: - case SimPuk: - // Shortcut for SIM PIN/PUK to go to directly to user's security screen or home - SecurityMode securityMode = mSecurityModel.getSecurityMode(targetUserId); - if (securityMode == SecurityMode.None && mLockPatternUtils.isLockScreenDisabled( - KeyguardUpdateMonitor.getCurrentUser())) { - finish = true; - eventSubtype = BOUNCER_DISMISS_SIM; - uiEvent = BouncerUiEvent.BOUNCER_DISMISS_SIM; - } else { - showSecurityScreen(securityMode); - } - break; - - default: - Log.v(TAG, "Bad security screen " + getCurrentSecurityMode() - + ", fail safe"); - showPrimarySecurityScreen(false); - break; - } - } - // Check for device admin specified additional security measures. - if (finish && !bypassSecondaryLockScreen) { - Intent secondaryLockscreenIntent = - mUpdateMonitor.getSecondaryLockscreenRequirement(targetUserId); - if (secondaryLockscreenIntent != null) { - mAdminSecondaryLockScreenController.show(secondaryLockscreenIntent); - return false; - } - } - if (eventSubtype != -1) { - mMetricsLogger.write(new LogMaker(MetricsProto.MetricsEvent.BOUNCER) - .setType(MetricsProto.MetricsEvent.TYPE_DISMISS).setSubtype(eventSubtype)); - } - if (uiEvent != BouncerUiEvent.UNKNOWN) { - mUiEventLogger.log(uiEvent); - } - if (finish) { - mSecurityCallback.finish(strongAuth, targetUserId); - } - return finish; + return mView.showNextSecurityScreenOrFinish( + authenticated, targetUserId, bypassSecondaryLockScreen); } public boolean needsInput() { - return getCurrentSecurityController().needsInput(); - } - - /** - * Switches to the given security view unless it's already being shown, in which case - * this is a no-op. - * - * @param securityMode - */ - @VisibleForTesting - void showSecurityScreen(SecurityMode securityMode) { - if (DEBUG) Log.d(TAG, "showSecurityScreen(" + securityMode + ")"); - - if (securityMode == SecurityMode.Invalid || securityMode == mCurrentSecurityMode) { - return; - } - - KeyguardInputViewController<KeyguardInputView> oldView = getCurrentSecurityController(); - - // Emulate Activity life cycle - if (oldView != null) { - oldView.onPause(); - } - - KeyguardInputViewController<KeyguardInputView> newView = changeSecurityMode(securityMode); - if (newView != null) { - newView.onResume(KeyguardSecurityView.VIEW_REVEALED); - mSecurityViewFlipperController.show(newView); - } - - mSecurityCallback.onSecurityModeChanged( - securityMode, newView != null && newView.needsInput()); - } - - public void reportFailedUnlockAttempt(int userId, int timeoutMs) { - // +1 for this time - final int failedAttempts = mLockPatternUtils.getCurrentFailedPasswordAttempts(userId) + 1; - - if (DEBUG) Log.d(TAG, "reportFailedPatternAttempt: #" + failedAttempts); - - final DevicePolicyManager dpm = mLockPatternUtils.getDevicePolicyManager(); - final int failedAttemptsBeforeWipe = - dpm.getMaximumFailedPasswordsForWipe(null, userId); - - final int remainingBeforeWipe = failedAttemptsBeforeWipe > 0 - ? (failedAttemptsBeforeWipe - failedAttempts) - : Integer.MAX_VALUE; // because DPM returns 0 if no restriction - if (remainingBeforeWipe < LockPatternUtils.FAILED_ATTEMPTS_BEFORE_WIPE_GRACE) { - // The user has installed a DevicePolicyManager that requests a user/profile to be wiped - // N attempts. Once we get below the grace period, we post this dialog every time as a - // clear warning until the deletion fires. - // Check which profile has the strictest policy for failed password attempts - final int expiringUser = dpm.getProfileWithMinimumFailedPasswordsForWipe(userId); - int userType = USER_TYPE_PRIMARY; - if (expiringUser == userId) { - // TODO: http://b/23522538 - if (expiringUser != UserHandle.USER_SYSTEM) { - userType = USER_TYPE_SECONDARY_USER; - } - } else if (expiringUser != UserHandle.USER_NULL) { - userType = USER_TYPE_WORK_PROFILE; - } // If USER_NULL, which shouldn't happen, leave it as USER_TYPE_PRIMARY - if (remainingBeforeWipe > 0) { - mView.showAlmostAtWipeDialog(failedAttempts, remainingBeforeWipe, userType); - } else { - // Too many attempts. The device will be wiped shortly. - Slog.i(TAG, "Too many unlock attempts; user " + expiringUser + " will be wiped!"); - mView.showWipeDialog(failedAttempts, userType); - } - } - mLockPatternUtils.reportFailedPasswordAttempt(userId); - if (timeoutMs > 0) { - mLockPatternUtils.reportPasswordLockout(timeoutMs, userId); - mView.showTimeoutDialog(userId, timeoutMs, mLockPatternUtils, - mSecurityModel.getSecurityMode(userId)); - } + return mView.needsInput(); } - private KeyguardInputViewController<KeyguardInputView> getCurrentSecurityController() { - return mSecurityViewFlipperController - .getSecurityView(mCurrentSecurityMode, mKeyguardSecurityCallback); - } - - private KeyguardInputViewController<KeyguardInputView> changeSecurityMode( - SecurityMode securityMode) { - mCurrentSecurityMode = securityMode; - return getCurrentSecurityController(); + public SecurityMode getCurrentSecurityMode() { + return mView.getCurrentSecurityMode(); } } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityModel.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityModel.java index c77c86711abf..ac2160ecb4ae 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityModel.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityModel.java @@ -18,14 +18,13 @@ package com.android.keyguard; import static com.android.systemui.DejankUtils.whitelistIpcs; import android.app.admin.DevicePolicyManager; -import android.content.res.Resources; +import android.content.Context; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; import com.android.internal.widget.LockPatternUtils; import com.android.systemui.Dependency; import com.android.systemui.dagger.SysUISingleton; -import com.android.systemui.dagger.qualifiers.Main; import javax.inject.Inject; @@ -34,7 +33,7 @@ public class KeyguardSecurityModel { /** * The different types of security available. - * @see KeyguardSecurityContainerController#showSecurityScreen + * @see KeyguardSecurityContainer#showSecurityScreen */ public enum SecurityMode { Invalid, // NULL state @@ -46,15 +45,21 @@ public class KeyguardSecurityModel { SimPuk // Unlock by entering a sim puk } + private final Context mContext; private final boolean mIsPukScreenAvailable; - private final LockPatternUtils mLockPatternUtils; + private LockPatternUtils mLockPatternUtils; @Inject - KeyguardSecurityModel(@Main Resources resources, LockPatternUtils lockPatternUtils) { - mIsPukScreenAvailable = resources.getBoolean( + KeyguardSecurityModel(Context context) { + mContext = context; + mLockPatternUtils = new LockPatternUtils(context); + mIsPukScreenAvailable = mContext.getResources().getBoolean( com.android.internal.R.bool.config_enable_puk_unlock_screen); - mLockPatternUtils = lockPatternUtils; + } + + void setLockPatternUtils(LockPatternUtils utils) { + mLockPatternUtils = utils; } public SecurityMode getSecurityMode(int userId) { diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityView.java index ac00e9453c97..43cef3acf147 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityView.java @@ -18,9 +18,11 @@ package com.android.keyguard; import android.content.res.ColorStateList; import android.view.MotionEvent; +import com.android.internal.widget.LockPatternUtils; + public interface KeyguardSecurityView { - int SCREEN_ON = 1; - int VIEW_REVEALED = 2; + static public final int SCREEN_ON = 1; + static public final int VIEW_REVEALED = 2; int PROMPT_REASON_NONE = 0; @@ -61,6 +63,18 @@ public interface KeyguardSecurityView { int PROMPT_REASON_NON_STRONG_BIOMETRIC_TIMEOUT = 7; /** + * Interface back to keyguard to tell it when security + * @param callback + */ + void setKeyguardCallback(KeyguardSecurityCallback callback); + + /** + * Set {@link LockPatternUtils} object. Useful for providing a mock interface. + * @param utils + */ + void setLockPatternUtils(LockPatternUtils utils); + + /** * Reset the view and prepare to take input. This should do things like clearing the * password or pattern and clear error messages. */ @@ -87,6 +101,12 @@ public interface KeyguardSecurityView { boolean needsInput(); /** + * Get {@link KeyguardSecurityCallback} for the given object + * @return KeyguardSecurityCallback + */ + KeyguardSecurityCallback getCallback(); + + /** * Show a string explaining why the security view needs to be solved. * * @param reason a flag indicating which string should be shown, see {@link #PROMPT_REASON_NONE} @@ -103,6 +123,12 @@ public interface KeyguardSecurityView { void showMessage(CharSequence message, ColorStateList colorState); /** + * Instruct the view to show usability hints, if any. + * + */ + void showUsabilityHint(); + + /** * Starts the animation which should run when the security view appears. */ void startAppearAnimation(); diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewController.java new file mode 100644 index 000000000000..ef9ba19fbb43 --- /dev/null +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewController.java @@ -0,0 +1,58 @@ +/* + * 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.keyguard; + +import android.view.View; + +import com.android.systemui.util.ViewController; + +import javax.inject.Inject; + + +/** Controller for a {@link KeyguardSecurityView}. */ +public class KeyguardSecurityViewController extends ViewController<View> { + + private final KeyguardSecurityView mView; + + private KeyguardSecurityViewController(KeyguardSecurityView view) { + super((View) view); + // KeyguardSecurityView isn't actually a View, so we need to track it ourselves. + mView = view; + } + + @Override + protected void onViewAttached() { + + } + + @Override + protected void onViewDetached() { + + } + + /** Factory for a {@link KeyguardSecurityViewController}. */ + public static class Factory { + @Inject + public Factory() { + } + + /** Create a new {@link KeyguardSecurityViewController}. */ + public KeyguardSecurityViewController create(KeyguardSecurityView view) { + return new KeyguardSecurityViewController(view); + } + } +} diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipper.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipper.java index b8439af6daaa..24da3ad46f23 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipper.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipper.java @@ -18,6 +18,7 @@ package com.android.keyguard; import android.annotation.NonNull; import android.content.Context; +import android.content.res.ColorStateList; import android.content.res.TypedArray; import android.graphics.Rect; import android.util.AttributeSet; @@ -30,6 +31,7 @@ import android.view.ViewHierarchyEncoder; import android.widget.FrameLayout; import android.widget.ViewFlipper; +import com.android.internal.widget.LockPatternUtils; import com.android.systemui.R; /** @@ -37,7 +39,7 @@ import com.android.systemui.R; * we can emulate {@link android.view.WindowManager.LayoutParams#FLAG_SLIPPERY} within a view * hierarchy. */ -public class KeyguardSecurityViewFlipper extends ViewFlipper { +public class KeyguardSecurityViewFlipper extends ViewFlipper implements KeyguardSecurityView { private static final String TAG = "KeyguardSecurityViewFlipper"; private static final boolean DEBUG = KeyguardConstants.DEBUG; @@ -67,16 +69,111 @@ public class KeyguardSecurityViewFlipper extends ViewFlipper { return result; } - KeyguardInputView getSecurityView() { + KeyguardSecurityView getSecurityView() { View child = getChildAt(getDisplayedChild()); - if (child instanceof KeyguardInputView) { - return (KeyguardInputView) child; + if (child instanceof KeyguardSecurityView) { + return (KeyguardSecurityView) child; } return null; } + @Override + public void setKeyguardCallback(KeyguardSecurityCallback callback) { + KeyguardSecurityView ksv = getSecurityView(); + if (ksv != null) { + ksv.setKeyguardCallback(callback); + } + } + + @Override + public void setLockPatternUtils(LockPatternUtils utils) { + KeyguardSecurityView ksv = getSecurityView(); + if (ksv != null) { + ksv.setLockPatternUtils(utils); + } + } + + @Override + public void reset() { + KeyguardSecurityView ksv = getSecurityView(); + if (ksv != null) { + ksv.reset(); + } + } + + @Override + public void onPause() { + KeyguardSecurityView ksv = getSecurityView(); + if (ksv != null) { + ksv.onPause(); + } + } + + @Override + public void onResume(int reason) { + KeyguardSecurityView ksv = getSecurityView(); + if (ksv != null) { + ksv.onResume(reason); + } + } + + @Override + public boolean needsInput() { + KeyguardSecurityView ksv = getSecurityView(); + return (ksv != null) ? ksv.needsInput() : false; + } + + @Override + public KeyguardSecurityCallback getCallback() { + KeyguardSecurityView ksv = getSecurityView(); + return (ksv != null) ? ksv.getCallback() : null; + } + + @Override + public void showPromptReason(int reason) { + KeyguardSecurityView ksv = getSecurityView(); + if (ksv != null) { + ksv.showPromptReason(reason); + } + } + + @Override + public void showMessage(CharSequence message, ColorStateList colorState) { + KeyguardSecurityView ksv = getSecurityView(); + if (ksv != null) { + ksv.showMessage(message, colorState); + } + } + + @Override + public void showUsabilityHint() { + KeyguardSecurityView ksv = getSecurityView(); + if (ksv != null) { + ksv.showUsabilityHint(); + } + } + + @Override + public void startAppearAnimation() { + KeyguardSecurityView ksv = getSecurityView(); + if (ksv != null) { + ksv.startAppearAnimation(); + } + } + + @Override + public boolean startDisappearAnimation(Runnable finishRunnable) { + KeyguardSecurityView ksv = getSecurityView(); + if (ksv != null) { + return ksv.startDisappearAnimation(finishRunnable); + } else { + return false; + } + } + + @Override public CharSequence getTitle() { - KeyguardInputView ksv = getSecurityView(); + KeyguardSecurityView ksv = getSecurityView(); if (ksv != null) { return ksv.getTitle(); } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipperController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipperController.java deleted file mode 100644 index 49530355a6fb..000000000000 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipperController.java +++ /dev/null @@ -1,148 +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.keyguard; - -import android.util.Log; -import android.view.LayoutInflater; - -import com.android.internal.annotations.VisibleForTesting; -import com.android.keyguard.KeyguardInputViewController.Factory; -import com.android.keyguard.KeyguardSecurityModel.SecurityMode; -import com.android.keyguard.dagger.KeyguardBouncerScope; -import com.android.systemui.R; -import com.android.systemui.util.ViewController; - -import java.util.ArrayList; -import java.util.List; - -import javax.inject.Inject; - -/** - * Controller for a {@link KeyguardSecurityViewFlipper}. - */ -@KeyguardBouncerScope -public class KeyguardSecurityViewFlipperController - extends ViewController<KeyguardSecurityViewFlipper> { - - private static final boolean DEBUG = KeyguardConstants.DEBUG; - private static final String TAG = "KeyguardSecurityView"; - - private final List<KeyguardInputViewController<KeyguardInputView>> mChildren = - new ArrayList<>(); - private final LayoutInflater mLayoutInflater; - private final Factory mKeyguardSecurityViewControllerFactory; - - @Inject - protected KeyguardSecurityViewFlipperController(KeyguardSecurityViewFlipper view, - LayoutInflater layoutInflater, - KeyguardInputViewController.Factory keyguardSecurityViewControllerFactory) { - super(view); - mKeyguardSecurityViewControllerFactory = keyguardSecurityViewControllerFactory; - mLayoutInflater = layoutInflater; - } - - @Override - protected void onViewAttached() { - - } - - @Override - protected void onViewDetached() { - - } - - public void reset() { - for (KeyguardInputViewController<KeyguardInputView> child : mChildren) { - child.reset(); - } - } - - @VisibleForTesting - KeyguardInputViewController<KeyguardInputView> getSecurityView(SecurityMode securityMode, - KeyguardSecurityCallback keyguardSecurityCallback) { - KeyguardInputViewController<KeyguardInputView> childController = null; - for (KeyguardInputViewController<KeyguardInputView> child : mChildren) { - if (child.getSecurityMode() == securityMode) { - childController = child; - break; - } - } - - if (childController == null - && securityMode != SecurityMode.None && securityMode != SecurityMode.Invalid) { - - int layoutId = getLayoutIdFor(securityMode); - KeyguardInputView view = null; - if (layoutId != 0) { - if (DEBUG) Log.v(TAG, "inflating id = " + layoutId); - view = (KeyguardInputView) mLayoutInflater.inflate( - layoutId, mView, false); - mView.addView(view); - childController = mKeyguardSecurityViewControllerFactory.create( - view, securityMode, keyguardSecurityCallback); - childController.init(); - - mChildren.add(childController); - } - } - - if (childController == null) { - childController = new NullKeyguardInputViewController( - securityMode, keyguardSecurityCallback); - } - - return childController; - } - - private int getLayoutIdFor(SecurityMode securityMode) { - switch (securityMode) { - case Pattern: return com.android.systemui.R.layout.keyguard_pattern_view; - case PIN: return com.android.systemui.R.layout.keyguard_pin_view; - case Password: return com.android.systemui.R.layout.keyguard_password_view; - case SimPin: return com.android.systemui.R.layout.keyguard_sim_pin_view; - case SimPuk: return R.layout.keyguard_sim_puk_view; - default: - return 0; - } - } - - /** Makes the supplied child visible if it is contained win this view, */ - public void show(KeyguardInputViewController<KeyguardInputView> childController) { - int index = childController.getIndexIn(mView); - if (index != -1) { - mView.setDisplayedChild(index); - } - } - - private static class NullKeyguardInputViewController - extends KeyguardInputViewController<KeyguardInputView> { - protected NullKeyguardInputViewController(SecurityMode securityMode, - KeyguardSecurityCallback keyguardSecurityCallback) { - super(null, securityMode, keyguardSecurityCallback); - } - - @Override - public boolean needsInput() { - return false; - } - - @Override - public void onStartingToHide() { - - } - } -} diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java index c0f9ce794628..1c47aa0151f0 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java @@ -16,19 +16,66 @@ package com.android.keyguard; +import android.annotation.NonNull; +import android.app.AlertDialog; +import android.app.AlertDialog.Builder; +import android.app.Dialog; +import android.app.ProgressDialog; import android.content.Context; +import android.content.res.ColorStateList; import android.content.res.Configuration; +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.graphics.Color; +import android.telephony.PinResult; +import android.telephony.SubscriptionInfo; +import android.telephony.SubscriptionManager; +import android.telephony.TelephonyManager; import android.util.AttributeSet; +import android.util.Log; import android.view.View; +import android.view.WindowManager; +import android.widget.ImageView; +import com.android.systemui.Dependency; import com.android.systemui.R; /** * Displays a PIN pad for unlocking. */ public class KeyguardSimPinView extends KeyguardPinBasedInputView { + private static final String LOG_TAG = "KeyguardSimPinView"; + private static final boolean DEBUG = KeyguardConstants.DEBUG_SIM_STATES; public static final String TAG = "KeyguardSimPinView"; + private ProgressDialog mSimUnlockProgressDialog = null; + private CheckSimPin mCheckSimPinThread; + + // Below flag is set to true during power-up or when a new SIM card inserted on device. + // When this is true and when SIM card is PIN locked state, on PIN lock screen, message would + // be displayed to inform user about the number of remaining PIN attempts left. + private boolean mShowDefaultMessage = true; + private int mRemainingAttempts = -1; + private AlertDialog mRemainingAttemptsDialog; + private int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; + private ImageView mSimImageView; + + KeyguardUpdateMonitorCallback mUpdateMonitorCallback = new KeyguardUpdateMonitorCallback() { + @Override + public void onSimStateChanged(int subId, int slotId, int simState) { + if (DEBUG) Log.v(TAG, "onSimStateChanged(subId=" + subId + ",state=" + simState + ")"); + switch(simState) { + case TelephonyManager.SIM_STATE_READY: { + mRemainingAttempts = -1; + resetState(); + break; + } + default: + resetState(); + } + } + }; + public KeyguardSimPinView(Context context) { this(context, null); } @@ -37,9 +84,81 @@ public class KeyguardSimPinView extends KeyguardPinBasedInputView { super(context, attrs); } - public void setEsimLocked(boolean locked) { + @Override + public void resetState() { + super.resetState(); + if (DEBUG) Log.v(TAG, "Resetting state"); + handleSubInfoChangeIfNeeded(); + if (mShowDefaultMessage) { + showDefaultMessage(); + } + boolean isEsimLocked = KeyguardEsimArea.isEsimLocked(mContext, mSubId); + KeyguardEsimArea esimButton = findViewById(R.id.keyguard_esim_area); - esimButton.setVisibility(locked ? View.VISIBLE : View.GONE); + esimButton.setVisibility(isEsimLocked ? View.VISIBLE : View.GONE); + } + + private void setLockedSimMessage() { + boolean isEsimLocked = KeyguardEsimArea.isEsimLocked(mContext, mSubId); + int count = 1; + TelephonyManager telephonyManager = + (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE); + if (telephonyManager != null) { + count = telephonyManager.getActiveModemCount(); + } + Resources rez = getResources(); + String msg; + TypedArray array = mContext.obtainStyledAttributes(new int[] { R.attr.wallpaperTextColor }); + int color = array.getColor(0, Color.WHITE); + array.recycle(); + if (count < 2) { + msg = rez.getString(R.string.kg_sim_pin_instructions); + } else { + SubscriptionInfo info = Dependency.get(KeyguardUpdateMonitor.class) + .getSubscriptionInfoForSubId(mSubId); + CharSequence displayName = info != null ? info.getDisplayName() : ""; // don't crash + msg = rez.getString(R.string.kg_sim_pin_instructions_multi, displayName); + if (info != null) { + color = info.getIconTint(); + } + } + if (isEsimLocked) { + msg = rez.getString(R.string.kg_sim_lock_esim_instructions, msg); + } + + if (mSecurityMessageDisplay != null && getVisibility() == VISIBLE) { + mSecurityMessageDisplay.setMessage(msg); + } + mSimImageView.setImageTintList(ColorStateList.valueOf(color)); + } + + private void showDefaultMessage() { + setLockedSimMessage(); + if (mRemainingAttempts >= 0) { + return; + } + + // Sending empty PIN here to query the number of remaining PIN attempts + new CheckSimPin("", mSubId) { + void onSimCheckResponse(final PinResult result) { + Log.d(LOG_TAG, "onSimCheckResponse " + " empty One result " + + result.toString()); + if (result.getAttemptsRemaining() >= 0) { + mRemainingAttempts = result.getAttemptsRemaining(); + setLockedSimMessage(); + } + } + }.start(); + } + + private void handleSubInfoChangeIfNeeded() { + KeyguardUpdateMonitor monitor = Dependency.get(KeyguardUpdateMonitor.class); + int subId = monitor.getNextSubIdForState(TelephonyManager.SIM_STATE_PIN_REQUIRED); + if (subId != mSubId && SubscriptionManager.isValidSubscriptionId(subId)) { + mSubId = subId; + mShowDefaultMessage = true; + mRemainingAttempts = -1; + } } @Override @@ -54,6 +173,35 @@ public class KeyguardSimPinView extends KeyguardPinBasedInputView { return 0; } + private String getPinPasswordErrorMessage(int attemptsRemaining, boolean isDefault) { + String displayMessage; + int msgId; + if (attemptsRemaining == 0) { + displayMessage = getContext().getString(R.string.kg_password_wrong_pin_code_pukked); + } else if (attemptsRemaining > 0) { + msgId = isDefault ? R.plurals.kg_password_default_pin_message : + R.plurals.kg_password_wrong_pin_code; + displayMessage = getContext().getResources() + .getQuantityString(msgId, attemptsRemaining, attemptsRemaining); + } else { + msgId = isDefault ? R.string.kg_sim_pin_instructions : R.string.kg_password_pin_failed; + displayMessage = getContext().getString(msgId); + } + if (KeyguardEsimArea.isEsimLocked(mContext, mSubId)) { + displayMessage = getResources() + .getString(R.string.kg_sim_lock_esim_instructions, displayMessage); + } + if (DEBUG) Log.d(LOG_TAG, "getPinPasswordErrorMessage:" + + " attemptsRemaining=" + attemptsRemaining + " displayMessage=" + displayMessage); + return displayMessage; + } + + @Override + protected boolean shouldLockout(long deadline) { + // SIM PIN doesn't have a timed lockout + return false; + } + @Override protected int getPasswordTextViewId() { return R.id.simPinEntry; @@ -66,6 +214,173 @@ public class KeyguardSimPinView extends KeyguardPinBasedInputView { if (mEcaView instanceof EmergencyCarrierArea) { ((EmergencyCarrierArea) mEcaView).setCarrierTextVisible(true); } + mSimImageView = findViewById(R.id.keyguard_sim); + } + + @Override + public void showUsabilityHint() { + + } + + @Override + public void onResume(int reason) { + super.onResume(reason); + Dependency.get(KeyguardUpdateMonitor.class).registerCallback(mUpdateMonitorCallback); + resetState(); + } + + @Override + public void onPause() { + // dismiss the dialog. + if (mSimUnlockProgressDialog != null) { + mSimUnlockProgressDialog.dismiss(); + mSimUnlockProgressDialog = null; + } + Dependency.get(KeyguardUpdateMonitor.class).removeCallback(mUpdateMonitorCallback); + } + + /** + * Since the IPC can block, we want to run the request in a separate thread + * with a callback. + */ + private abstract class CheckSimPin extends Thread { + private final String mPin; + private int mSubId; + + protected CheckSimPin(String pin, int subId) { + mPin = pin; + mSubId = subId; + } + + abstract void onSimCheckResponse(@NonNull PinResult result); + + @Override + public void run() { + if (DEBUG) { + Log.v(TAG, "call supplyPinReportResultForSubscriber(subid=" + mSubId + ")"); + } + TelephonyManager telephonyManager = + ((TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE)) + .createForSubscriptionId(mSubId); + final PinResult result = telephonyManager.supplyPinReportPinResult(mPin); + if (result == null) { + Log.e(TAG, "Error result for supplyPinReportResult."); + post(new Runnable() { + @Override + public void run() { + onSimCheckResponse(PinResult.getDefaultFailedResult()); + } + }); + } else { + if (DEBUG) { + Log.v(TAG, "supplyPinReportResult returned: " + result.toString()); + } + post(new Runnable() { + @Override + public void run() { + onSimCheckResponse(result); + } + }); + } + } + } + + private Dialog getSimUnlockProgressDialog() { + if (mSimUnlockProgressDialog == null) { + mSimUnlockProgressDialog = new ProgressDialog(mContext); + mSimUnlockProgressDialog.setMessage( + mContext.getString(R.string.kg_sim_unlock_progress_dialog_message)); + mSimUnlockProgressDialog.setIndeterminate(true); + mSimUnlockProgressDialog.setCancelable(false); + mSimUnlockProgressDialog.getWindow().setType( + WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); + } + return mSimUnlockProgressDialog; + } + + private Dialog getSimRemainingAttemptsDialog(int remaining) { + String msg = getPinPasswordErrorMessage(remaining, false); + if (mRemainingAttemptsDialog == null) { + Builder builder = new AlertDialog.Builder(mContext); + builder.setMessage(msg); + builder.setCancelable(false); + builder.setNeutralButton(R.string.ok, null); + mRemainingAttemptsDialog = builder.create(); + mRemainingAttemptsDialog.getWindow().setType( + WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); + } else { + mRemainingAttemptsDialog.setMessage(msg); + } + return mRemainingAttemptsDialog; + } + + @Override + protected void verifyPasswordAndUnlock() { + String entry = mPasswordEntry.getText(); + + if (entry.length() < 4) { + // otherwise, display a message to the user, and don't submit. + mSecurityMessageDisplay.setMessage(R.string.kg_invalid_sim_pin_hint); + resetPasswordText(true /* animate */, true /* announce */); + mCallback.userActivity(); + return; + } + + getSimUnlockProgressDialog().show(); + + if (mCheckSimPinThread == null) { + mCheckSimPinThread = new CheckSimPin(mPasswordEntry.getText(), mSubId) { + @Override + void onSimCheckResponse(final PinResult result) { + post(new Runnable() { + @Override + public void run() { + mRemainingAttempts = result.getAttemptsRemaining(); + if (mSimUnlockProgressDialog != null) { + mSimUnlockProgressDialog.hide(); + } + resetPasswordText(true /* animate */, + /* announce */ + result.getType() != PinResult.PIN_RESULT_TYPE_SUCCESS); + if (result.getType() == PinResult.PIN_RESULT_TYPE_SUCCESS) { + Dependency.get(KeyguardUpdateMonitor.class) + .reportSimUnlocked(mSubId); + mRemainingAttempts = -1; + mShowDefaultMessage = true; + if (mCallback != null) { + mCallback.dismiss(true, KeyguardUpdateMonitor.getCurrentUser()); + } + } else { + mShowDefaultMessage = false; + if (result.getType() == PinResult.PIN_RESULT_TYPE_INCORRECT) { + if (result.getAttemptsRemaining() <= 2) { + // this is getting critical - show dialog + getSimRemainingAttemptsDialog( + result.getAttemptsRemaining()).show(); + } else { + // show message + mSecurityMessageDisplay.setMessage( + getPinPasswordErrorMessage( + result.getAttemptsRemaining(), false)); + } + } else { + // "PIN operation failed!" - no idea what this was and no way to + // find out. :/ + mSecurityMessageDisplay.setMessage(getContext().getString( + R.string.kg_password_pin_failed)); + } + if (DEBUG) Log.d(LOG_TAG, "verifyPasswordAndUnlock " + + " CheckSimPin.onSimCheckResponse: " + result + + " attemptsRemaining=" + result.getAttemptsRemaining()); + } + mCallback.userActivity(); + mCheckSimPinThread = null; + } + }); + } + }; + mCheckSimPinThread.start(); + } } @Override @@ -74,6 +389,11 @@ public class KeyguardSimPinView extends KeyguardPinBasedInputView { } @Override + public boolean startDisappearAnimation(Runnable finishRunnable) { + return false; + } + + @Override public CharSequence getTitle() { return getContext().getString( com.android.internal.R.string.keyguard_accessibility_sim_pin_unlock); diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java deleted file mode 100644 index cc8bf4f2d028..000000000000 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java +++ /dev/null @@ -1,350 +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.keyguard; - -import android.annotation.NonNull; -import android.app.AlertDialog; -import android.app.AlertDialog.Builder; -import android.app.Dialog; -import android.app.ProgressDialog; -import android.content.res.ColorStateList; -import android.content.res.Resources; -import android.content.res.TypedArray; -import android.graphics.Color; -import android.telephony.PinResult; -import android.telephony.SubscriptionInfo; -import android.telephony.SubscriptionManager; -import android.telephony.TelephonyManager; -import android.util.Log; -import android.view.View; -import android.view.WindowManager; -import android.widget.ImageView; - -import com.android.internal.util.LatencyTracker; -import com.android.internal.widget.LockPatternUtils; -import com.android.keyguard.KeyguardSecurityModel.SecurityMode; -import com.android.systemui.R; - -public class KeyguardSimPinViewController - extends KeyguardPinBasedInputViewController<KeyguardSimPinView> { - public static final String TAG = "KeyguardSimPinView"; - private static final String LOG_TAG = "KeyguardSimPinView"; - private static final boolean DEBUG = KeyguardConstants.DEBUG_SIM_STATES; - private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; - private final TelephonyManager mTelephonyManager; - - private ProgressDialog mSimUnlockProgressDialog; - private CheckSimPin mCheckSimPinThread; - private int mRemainingAttempts; - // Below flag is set to true during power-up or when a new SIM card inserted on device. - // When this is true and when SIM card is PIN locked state, on PIN lock screen, message would - // be displayed to inform user about the number of remaining PIN attempts left. - private boolean mShowDefaultMessage; - private int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; - private AlertDialog mRemainingAttemptsDialog; - private ImageView mSimImageView; - - KeyguardUpdateMonitorCallback mUpdateMonitorCallback = new KeyguardUpdateMonitorCallback() { - @Override - public void onSimStateChanged(int subId, int slotId, int simState) { - if (DEBUG) Log.v(TAG, "onSimStateChanged(subId=" + subId + ",state=" + simState + ")"); - if (simState == TelephonyManager.SIM_STATE_READY) { - mRemainingAttempts = -1; - resetState(); - } else { - resetState(); - } - } - }; - - protected KeyguardSimPinViewController(KeyguardSimPinView view, - KeyguardUpdateMonitor keyguardUpdateMonitor, - SecurityMode securityMode, LockPatternUtils lockPatternUtils, - KeyguardSecurityCallback keyguardSecurityCallback, - KeyguardMessageAreaController.Factory messageAreaControllerFactory, - LatencyTracker latencyTracker, - LiftToActivateListener liftToActivateListener, - TelephonyManager telephonyManager) { - super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback, - messageAreaControllerFactory, latencyTracker, liftToActivateListener); - mKeyguardUpdateMonitor = keyguardUpdateMonitor; - mTelephonyManager = telephonyManager; - mSimImageView = mView.findViewById(R.id.keyguard_sim); - } - - @Override - protected void onViewAttached() { - super.onViewAttached(); - } - - @Override - void resetState() { - super.resetState(); - if (DEBUG) Log.v(TAG, "Resetting state"); - handleSubInfoChangeIfNeeded(); - mMessageAreaController.setMessage(""); - if (mShowDefaultMessage) { - showDefaultMessage(); - } - - mView.setEsimLocked(KeyguardEsimArea.isEsimLocked(mView.getContext(), mSubId)); - } - - @Override - public boolean startDisappearAnimation(Runnable finishRunnable) { - return false; - } - - @Override - public void onResume(int reason) { - super.onResume(reason); - mKeyguardUpdateMonitor.registerCallback(mUpdateMonitorCallback); - mView.resetState(); - } - - @Override - public void onPause() { - super.onPause(); - mKeyguardUpdateMonitor.removeCallback(mUpdateMonitorCallback); - - // dismiss the dialog. - if (mSimUnlockProgressDialog != null) { - mSimUnlockProgressDialog.dismiss(); - mSimUnlockProgressDialog = null; - } - } - - @Override - protected void verifyPasswordAndUnlock() { - String entry = mPasswordEntry.getText(); - - if (entry.length() < 4) { - // otherwise, display a message to the user, and don't submit. - mMessageAreaController.setMessage( - com.android.systemui.R.string.kg_invalid_sim_pin_hint); - mView.resetPasswordText(true /* animate */, true /* announce */); - getKeyguardSecurityCallback().userActivity(); - return; - } - - getSimUnlockProgressDialog().show(); - - if (mCheckSimPinThread == null) { - mCheckSimPinThread = new CheckSimPin(mPasswordEntry.getText(), mSubId) { - @Override - void onSimCheckResponse(final PinResult result) { - mView.post(() -> { - mRemainingAttempts = result.getAttemptsRemaining(); - if (mSimUnlockProgressDialog != null) { - mSimUnlockProgressDialog.hide(); - } - mView.resetPasswordText(true /* animate */, - /* announce */ - result.getType() != PinResult.PIN_RESULT_TYPE_SUCCESS); - if (result.getType() == PinResult.PIN_RESULT_TYPE_SUCCESS) { - mKeyguardUpdateMonitor.reportSimUnlocked(mSubId); - mRemainingAttempts = -1; - mShowDefaultMessage = true; - getKeyguardSecurityCallback().dismiss( - true, KeyguardUpdateMonitor.getCurrentUser()); - } else { - mShowDefaultMessage = false; - if (result.getType() == PinResult.PIN_RESULT_TYPE_INCORRECT) { - if (result.getAttemptsRemaining() <= 2) { - // this is getting critical - show dialog - getSimRemainingAttemptsDialog( - result.getAttemptsRemaining()).show(); - } else { - // show message - mMessageAreaController.setMessage( - getPinPasswordErrorMessage( - result.getAttemptsRemaining(), false)); - } - } else { - // "PIN operation failed!" - no idea what this was and no way to - // find out. :/ - mMessageAreaController.setMessage(mView.getResources().getString( - R.string.kg_password_pin_failed)); - } - if (DEBUG) { - Log.d(LOG_TAG, "verifyPasswordAndUnlock " - + " CheckSimPin.onSimCheckResponse: " + result - + " attemptsRemaining=" + result.getAttemptsRemaining()); - } - } - getKeyguardSecurityCallback().userActivity(); - mCheckSimPinThread = null; - }); - } - }; - mCheckSimPinThread.start(); - } - } - - private Dialog getSimUnlockProgressDialog() { - if (mSimUnlockProgressDialog == null) { - mSimUnlockProgressDialog = new ProgressDialog(mView.getContext()); - mSimUnlockProgressDialog.setMessage( - mView.getResources().getString(R.string.kg_sim_unlock_progress_dialog_message)); - mSimUnlockProgressDialog.setIndeterminate(true); - mSimUnlockProgressDialog.setCancelable(false); - mSimUnlockProgressDialog.getWindow().setType( - WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); - } - return mSimUnlockProgressDialog; - } - - - private Dialog getSimRemainingAttemptsDialog(int remaining) { - String msg = getPinPasswordErrorMessage(remaining, false); - if (mRemainingAttemptsDialog == null) { - Builder builder = new AlertDialog.Builder(mView.getContext()); - builder.setMessage(msg); - builder.setCancelable(false); - builder.setNeutralButton(R.string.ok, null); - mRemainingAttemptsDialog = builder.create(); - mRemainingAttemptsDialog.getWindow().setType( - WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); - } else { - mRemainingAttemptsDialog.setMessage(msg); - } - return mRemainingAttemptsDialog; - } - - - private String getPinPasswordErrorMessage(int attemptsRemaining, boolean isDefault) { - String displayMessage; - int msgId; - if (attemptsRemaining == 0) { - displayMessage = mView.getResources().getString( - R.string.kg_password_wrong_pin_code_pukked); - } else if (attemptsRemaining > 0) { - msgId = isDefault ? R.plurals.kg_password_default_pin_message : - R.plurals.kg_password_wrong_pin_code; - displayMessage = mView.getResources() - .getQuantityString(msgId, attemptsRemaining, attemptsRemaining); - } else { - msgId = isDefault ? R.string.kg_sim_pin_instructions : R.string.kg_password_pin_failed; - displayMessage = mView.getResources().getString(msgId); - } - if (KeyguardEsimArea.isEsimLocked(mView.getContext(), mSubId)) { - displayMessage = mView.getResources() - .getString(R.string.kg_sim_lock_esim_instructions, displayMessage); - } - if (DEBUG) { - Log.d(LOG_TAG, "getPinPasswordErrorMessage: attemptsRemaining=" - + attemptsRemaining + " displayMessage=" + displayMessage); - } - return displayMessage; - } - - private void showDefaultMessage() { - setLockedSimMessage(); - if (mRemainingAttempts >= 0) { - return; - } - - // Sending empty PIN here to query the number of remaining PIN attempts - new CheckSimPin("", mSubId) { - void onSimCheckResponse(final PinResult result) { - Log.d(LOG_TAG, "onSimCheckResponse " + " empty One result " - + result.toString()); - if (result.getAttemptsRemaining() >= 0) { - mRemainingAttempts = result.getAttemptsRemaining(); - setLockedSimMessage(); - } - } - }.start(); - } - - /** - * Since the IPC can block, we want to run the request in a separate thread - * with a callback. - */ - private abstract class CheckSimPin extends Thread { - private final String mPin; - private int mSubId; - - protected CheckSimPin(String pin, int subId) { - mPin = pin; - mSubId = subId; - } - - abstract void onSimCheckResponse(@NonNull PinResult result); - - @Override - public void run() { - if (DEBUG) { - Log.v(TAG, "call supplyPinReportResultForSubscriber(subid=" + mSubId + ")"); - } - TelephonyManager telephonyManager = - mTelephonyManager.createForSubscriptionId(mSubId); - final PinResult result = telephonyManager.supplyPinReportPinResult(mPin); - if (result == null) { - Log.e(TAG, "Error result for supplyPinReportResult."); - mView.post(() -> onSimCheckResponse(PinResult.getDefaultFailedResult())); - } else { - if (DEBUG) { - Log.v(TAG, "supplyPinReportResult returned: " + result.toString()); - } - mView.post(() -> onSimCheckResponse(result)); - } - } - } - - private void setLockedSimMessage() { - boolean isEsimLocked = KeyguardEsimArea.isEsimLocked(mView.getContext(), mSubId); - int count = 1; - if (mTelephonyManager != null) { - count = mTelephonyManager.getActiveModemCount(); - } - Resources rez = mView.getResources(); - String msg; - TypedArray array = mView.getContext().obtainStyledAttributes( - new int[] { R.attr.wallpaperTextColor }); - int color = array.getColor(0, Color.WHITE); - array.recycle(); - if (count < 2) { - msg = rez.getString(R.string.kg_sim_pin_instructions); - } else { - SubscriptionInfo info = mKeyguardUpdateMonitor.getSubscriptionInfoForSubId(mSubId); - CharSequence displayName = info != null ? info.getDisplayName() : ""; // don't crash - msg = rez.getString(R.string.kg_sim_pin_instructions_multi, displayName); - if (info != null) { - color = info.getIconTint(); - } - } - if (isEsimLocked) { - msg = rez.getString(R.string.kg_sim_lock_esim_instructions, msg); - } - - if (mView.getVisibility() == View.VISIBLE) { - mMessageAreaController.setMessage(msg); - } - mSimImageView.setImageTintList(ColorStateList.valueOf(color)); - } - - private void handleSubInfoChangeIfNeeded() { - int subId = mKeyguardUpdateMonitor - .getNextSubIdForState(TelephonyManager.SIM_STATE_PIN_REQUIRED); - if (subId != mSubId && SubscriptionManager.isValidSubscriptionId(subId)) { - mSubId = subId; - mShowDefaultMessage = true; - mRemainingAttempts = -1; - } - } -} diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java index 0d72c93e9041..5148dd709026 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java @@ -16,10 +16,27 @@ package com.android.keyguard; +import android.annotation.NonNull; +import android.app.Activity; +import android.app.AlertDialog; +import android.app.Dialog; +import android.app.ProgressDialog; import android.content.Context; +import android.content.res.ColorStateList; +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.graphics.Color; +import android.telephony.PinResult; +import android.telephony.SubscriptionInfo; +import android.telephony.SubscriptionManager; +import android.telephony.TelephonyManager; import android.util.AttributeSet; import android.util.Log; +import android.view.View; +import android.view.WindowManager; +import android.widget.ImageView; +import com.android.systemui.Dependency; import com.android.systemui.R; @@ -27,9 +44,48 @@ import com.android.systemui.R; * Displays a PIN pad for entering a PUK (Pin Unlock Kode) provided by a carrier. */ public class KeyguardSimPukView extends KeyguardPinBasedInputView { + private static final String LOG_TAG = "KeyguardSimPukView"; private static final boolean DEBUG = KeyguardConstants.DEBUG; public static final String TAG = "KeyguardSimPukView"; + private ProgressDialog mSimUnlockProgressDialog = null; + private CheckSimPuk mCheckSimPukThread; + + // Below flag is set to true during power-up or when a new SIM card inserted on device. + // When this is true and when SIM card is PUK locked state, on PIN lock screen, message would + // be displayed to inform user about the number of remaining PUK attempts left. + private boolean mShowDefaultMessage = true; + private int mRemainingAttempts = -1; + private String mPukText; + private String mPinText; + private StateMachine mStateMachine = new StateMachine(); + private AlertDialog mRemainingAttemptsDialog; + private int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; + private ImageView mSimImageView; + + KeyguardUpdateMonitorCallback mUpdateMonitorCallback = new KeyguardUpdateMonitorCallback() { + @Override + public void onSimStateChanged(int subId, int slotId, int simState) { + if (DEBUG) Log.v(TAG, "onSimStateChanged(subId=" + subId + ",state=" + simState + ")"); + switch(simState) { + // If the SIM is unlocked via a key sequence through the emergency dialer, it will + // move into the READY state and the PUK lock keyguard should be removed. + case TelephonyManager.SIM_STATE_READY: { + mRemainingAttempts = -1; + mShowDefaultMessage = true; + // mCallback can be null if onSimStateChanged callback is called when keyguard + // isn't active. + if (mCallback != null) { + mCallback.dismiss(true, KeyguardUpdateMonitor.getCurrentUser()); + } + break; + } + default: + resetState(); + } + } + }; + public KeyguardSimPukView(Context context) { this(context, null); } @@ -38,14 +94,136 @@ public class KeyguardSimPukView extends KeyguardPinBasedInputView { super(context, attrs); } + private class StateMachine { + final int ENTER_PUK = 0; + final int ENTER_PIN = 1; + final int CONFIRM_PIN = 2; + final int DONE = 3; + private int state = ENTER_PUK; + + public void next() { + int msg = 0; + if (state == ENTER_PUK) { + if (checkPuk()) { + state = ENTER_PIN; + msg = R.string.kg_puk_enter_pin_hint; + } else { + msg = R.string.kg_invalid_sim_puk_hint; + } + } else if (state == ENTER_PIN) { + if (checkPin()) { + state = CONFIRM_PIN; + msg = R.string.kg_enter_confirm_pin_hint; + } else { + msg = R.string.kg_invalid_sim_pin_hint; + } + } else if (state == CONFIRM_PIN) { + if (confirmPin()) { + state = DONE; + msg = R.string.keyguard_sim_unlock_progress_dialog_message; + updateSim(); + } else { + state = ENTER_PIN; // try again? + msg = R.string.kg_invalid_confirm_pin_hint; + } + } + resetPasswordText(true /* animate */, true /* announce */); + if (msg != 0) { + mSecurityMessageDisplay.setMessage(msg); + } + } + + + void reset() { + mPinText=""; + mPukText=""; + state = ENTER_PUK; + handleSubInfoChangeIfNeeded(); + if (mShowDefaultMessage) { + showDefaultMessage(); + } + boolean isEsimLocked = KeyguardEsimArea.isEsimLocked(mContext, mSubId); + + KeyguardEsimArea esimButton = findViewById(R.id.keyguard_esim_area); + esimButton.setVisibility(isEsimLocked ? View.VISIBLE : View.GONE); + mPasswordEntry.requestFocus(); + } + + + } + + private void showDefaultMessage() { + if (mRemainingAttempts >= 0) { + mSecurityMessageDisplay.setMessage(getPukPasswordErrorMessage( + mRemainingAttempts, true)); + return; + } + + boolean isEsimLocked = KeyguardEsimArea.isEsimLocked(mContext, mSubId); + int count = 1; + TelephonyManager telephonyManager = + (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE); + if (telephonyManager != null) { + count = telephonyManager.getActiveModemCount(); + } + Resources rez = getResources(); + String msg; + TypedArray array = mContext.obtainStyledAttributes(new int[] { R.attr.wallpaperTextColor }); + int color = array.getColor(0, Color.WHITE); + array.recycle(); + if (count < 2) { + msg = rez.getString(R.string.kg_puk_enter_puk_hint); + } else { + SubscriptionInfo info = Dependency.get(KeyguardUpdateMonitor.class) + .getSubscriptionInfoForSubId(mSubId); + CharSequence displayName = info != null ? info.getDisplayName() : ""; + msg = rez.getString(R.string.kg_puk_enter_puk_hint_multi, displayName); + if (info != null) { + color = info.getIconTint(); + } + } + if (isEsimLocked) { + msg = rez.getString(R.string.kg_sim_lock_esim_instructions, msg); + } + if (mSecurityMessageDisplay != null) { + mSecurityMessageDisplay.setMessage(msg); + } + mSimImageView.setImageTintList(ColorStateList.valueOf(color)); + + // Sending empty PUK here to query the number of remaining PIN attempts + new CheckSimPuk("", "", mSubId) { + void onSimLockChangedResponse(final PinResult result) { + if (result == null) Log.e(LOG_TAG, "onSimCheckResponse, pin result is NULL"); + else { + Log.d(LOG_TAG, "onSimCheckResponse " + " empty One result " + + result.toString()); + if (result.getAttemptsRemaining() >= 0) { + mRemainingAttempts = result.getAttemptsRemaining(); + mSecurityMessageDisplay.setMessage( + getPukPasswordErrorMessage(result.getAttemptsRemaining(), true)); + } + } + } + }.start(); + } + + private void handleSubInfoChangeIfNeeded() { + KeyguardUpdateMonitor monitor = Dependency.get(KeyguardUpdateMonitor.class); + int subId = monitor.getNextSubIdForState(TelephonyManager.SIM_STATE_PUK_REQUIRED); + if (subId != mSubId && SubscriptionManager.isValidSubscriptionId(subId)) { + mSubId = subId; + mShowDefaultMessage = true; + mRemainingAttempts = -1; + } + } + @Override protected int getPromptReasonStringRes(int reason) { // No message on SIM Puk return 0; } - String getPukPasswordErrorMessage( - int attemptsRemaining, boolean isDefault, boolean isEsimLocked) { + private String getPukPasswordErrorMessage(int attemptsRemaining, boolean isDefault) { String displayMessage; if (attemptsRemaining == 0) { @@ -60,19 +238,28 @@ public class KeyguardSimPukView extends KeyguardPinBasedInputView { R.string.kg_password_puk_failed; displayMessage = getContext().getString(msgId); } - if (isEsimLocked) { + if (KeyguardEsimArea.isEsimLocked(mContext, mSubId)) { displayMessage = getResources() .getString(R.string.kg_sim_lock_esim_instructions, displayMessage); } - if (DEBUG) { - Log.d(TAG, "getPukPasswordErrorMessage:" - + " attemptsRemaining=" + attemptsRemaining - + " displayMessage=" + displayMessage); - } + if (DEBUG) Log.d(LOG_TAG, "getPukPasswordErrorMessage:" + + " attemptsRemaining=" + attemptsRemaining + " displayMessage=" + displayMessage); return displayMessage; } @Override + public void resetState() { + super.resetState(); + mStateMachine.reset(); + } + + @Override + protected boolean shouldLockout(long deadline) { + // SIM PUK doesn't have a timed lockout + return false; + } + + @Override protected int getPasswordTextViewId() { return R.id.pukEntry; } @@ -84,6 +271,197 @@ public class KeyguardSimPukView extends KeyguardPinBasedInputView { if (mEcaView instanceof EmergencyCarrierArea) { ((EmergencyCarrierArea) mEcaView).setCarrierTextVisible(true); } + mSimImageView = findViewById(R.id.keyguard_sim); + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + Dependency.get(KeyguardUpdateMonitor.class).registerCallback(mUpdateMonitorCallback); + resetState(); + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + Dependency.get(KeyguardUpdateMonitor.class).removeCallback(mUpdateMonitorCallback); + } + + @Override + public void showUsabilityHint() { + } + + @Override + public void onPause() { + // dismiss the dialog. + if (mSimUnlockProgressDialog != null) { + mSimUnlockProgressDialog.dismiss(); + mSimUnlockProgressDialog = null; + } + } + + /** + * Since the IPC can block, we want to run the request in a separate thread + * with a callback. + */ + private abstract class CheckSimPuk extends Thread { + + private final String mPin, mPuk; + private final int mSubId; + + protected CheckSimPuk(String puk, String pin, int subId) { + mPuk = puk; + mPin = pin; + mSubId = subId; + } + + abstract void onSimLockChangedResponse(@NonNull PinResult result); + + @Override + public void run() { + if (DEBUG) Log.v(TAG, "call supplyPukReportResult()"); + TelephonyManager telephonyManager = + ((TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE)) + .createForSubscriptionId(mSubId); + final PinResult result = telephonyManager.supplyPukReportPinResult(mPuk, mPin); + if (result == null) { + Log.e(TAG, "Error result for supplyPukReportResult."); + post(new Runnable() { + @Override + public void run() { + onSimLockChangedResponse(PinResult.getDefaultFailedResult()); + } + }); + } else { + if (DEBUG) { + Log.v(TAG, "supplyPukReportResult returned: " + result.toString()); + } + post(new Runnable() { + @Override + public void run() { + onSimLockChangedResponse(result); + } + }); + } + } + } + + private Dialog getSimUnlockProgressDialog() { + if (mSimUnlockProgressDialog == null) { + mSimUnlockProgressDialog = new ProgressDialog(mContext); + mSimUnlockProgressDialog.setMessage( + mContext.getString(R.string.kg_sim_unlock_progress_dialog_message)); + mSimUnlockProgressDialog.setIndeterminate(true); + mSimUnlockProgressDialog.setCancelable(false); + if (!(mContext instanceof Activity)) { + mSimUnlockProgressDialog.getWindow().setType( + WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); + } + } + return mSimUnlockProgressDialog; + } + + private Dialog getPukRemainingAttemptsDialog(int remaining) { + String msg = getPukPasswordErrorMessage(remaining, false); + if (mRemainingAttemptsDialog == null) { + AlertDialog.Builder builder = new AlertDialog.Builder(mContext); + builder.setMessage(msg); + builder.setCancelable(false); + builder.setNeutralButton(R.string.ok, null); + mRemainingAttemptsDialog = builder.create(); + mRemainingAttemptsDialog.getWindow().setType( + WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); + } else { + mRemainingAttemptsDialog.setMessage(msg); + } + return mRemainingAttemptsDialog; + } + + private boolean checkPuk() { + // make sure the puk is at least 8 digits long. + if (mPasswordEntry.getText().length() == 8) { + mPukText = mPasswordEntry.getText(); + return true; + } + return false; + } + + private boolean checkPin() { + // make sure the PIN is between 4 and 8 digits + int length = mPasswordEntry.getText().length(); + if (length >= 4 && length <= 8) { + mPinText = mPasswordEntry.getText(); + return true; + } + return false; + } + + public boolean confirmPin() { + return mPinText.equals(mPasswordEntry.getText()); + } + + private void updateSim() { + getSimUnlockProgressDialog().show(); + + if (mCheckSimPukThread == null) { + mCheckSimPukThread = new CheckSimPuk(mPukText, mPinText, mSubId) { + @Override + void onSimLockChangedResponse(final PinResult result) { + post(new Runnable() { + @Override + public void run() { + if (mSimUnlockProgressDialog != null) { + mSimUnlockProgressDialog.hide(); + } + resetPasswordText(true /* animate */, + /* announce */ + result.getType() != PinResult.PIN_RESULT_TYPE_SUCCESS); + if (result.getType() == PinResult.PIN_RESULT_TYPE_SUCCESS) { + Dependency.get(KeyguardUpdateMonitor.class) + .reportSimUnlocked(mSubId); + mRemainingAttempts = -1; + mShowDefaultMessage = true; + if (mCallback != null) { + mCallback.dismiss(true, + KeyguardUpdateMonitor.getCurrentUser()); + } + } else { + mShowDefaultMessage = false; + if (result.getType() == PinResult.PIN_RESULT_TYPE_INCORRECT) { + // show message + mSecurityMessageDisplay.setMessage(getPukPasswordErrorMessage( + result.getAttemptsRemaining(), false)); + if (result.getAttemptsRemaining() <= 2) { + // this is getting critical - show dialog + getPukRemainingAttemptsDialog( + result.getAttemptsRemaining()).show(); + } else { + // show message + mSecurityMessageDisplay.setMessage( + getPukPasswordErrorMessage( + result.getAttemptsRemaining(), false)); + } + } else { + mSecurityMessageDisplay.setMessage(getContext().getString( + R.string.kg_password_puk_failed)); + } + if (DEBUG) Log.d(LOG_TAG, "verifyPasswordAndUnlock " + + " UpdateSim.onSimCheckResponse: " + + " attemptsRemaining=" + result.getAttemptsRemaining()); + } + mStateMachine.reset(); + mCheckSimPukThread = null; + } + }); + } + }; + mCheckSimPukThread.start(); + } + } + + @Override + protected void verifyPasswordAndUnlock() { + mStateMachine.next(); } @Override diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java deleted file mode 100644 index a87374939ba6..000000000000 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java +++ /dev/null @@ -1,413 +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.keyguard; - -import android.annotation.NonNull; -import android.app.Activity; -import android.app.AlertDialog; -import android.app.Dialog; -import android.app.ProgressDialog; -import android.content.res.ColorStateList; -import android.content.res.Resources; -import android.content.res.TypedArray; -import android.graphics.Color; -import android.telephony.PinResult; -import android.telephony.SubscriptionInfo; -import android.telephony.SubscriptionManager; -import android.telephony.TelephonyManager; -import android.util.Log; -import android.view.View; -import android.view.WindowManager; -import android.widget.ImageView; - -import com.android.internal.util.LatencyTracker; -import com.android.internal.widget.LockPatternUtils; -import com.android.keyguard.KeyguardSecurityModel.SecurityMode; -import com.android.systemui.Dependency; -import com.android.systemui.R; - -public class KeyguardSimPukViewController - extends KeyguardPinBasedInputViewController<KeyguardSimPukView> { - private static final boolean DEBUG = KeyguardConstants.DEBUG; - public static final String TAG = "KeyguardSimPukView"; - - private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; - private final TelephonyManager mTelephonyManager; - - private String mPukText; - private String mPinText; - private int mRemainingAttempts; - // Below flag is set to true during power-up or when a new SIM card inserted on device. - // When this is true and when SIM card is PUK locked state, on PIN lock screen, message would - // be displayed to inform user about the number of remaining PUK attempts left. - private boolean mShowDefaultMessage; - private StateMachine mStateMachine = new StateMachine(); - private int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; - private CheckSimPuk mCheckSimPukThread; - private ProgressDialog mSimUnlockProgressDialog; - - KeyguardUpdateMonitorCallback mUpdateMonitorCallback = new KeyguardUpdateMonitorCallback() { - @Override - public void onSimStateChanged(int subId, int slotId, int simState) { - if (DEBUG) Log.v(TAG, "onSimStateChanged(subId=" + subId + ",state=" + simState + ")"); - // If the SIM is unlocked via a key sequence through the emergency dialer, it will - // move into the READY state and the PUK lock keyguard should be removed. - if (simState == TelephonyManager.SIM_STATE_READY) { - mRemainingAttempts = -1; - mShowDefaultMessage = true; - getKeyguardSecurityCallback().dismiss(true, KeyguardUpdateMonitor.getCurrentUser()); - } else { - resetState(); - } - } - }; - private ImageView mSimImageView; - private AlertDialog mRemainingAttemptsDialog; - - protected KeyguardSimPukViewController(KeyguardSimPukView view, - KeyguardUpdateMonitor keyguardUpdateMonitor, - SecurityMode securityMode, LockPatternUtils lockPatternUtils, - KeyguardSecurityCallback keyguardSecurityCallback, - KeyguardMessageAreaController.Factory messageAreaControllerFactory, - LatencyTracker latencyTracker, - LiftToActivateListener liftToActivateListener, - TelephonyManager telephonyManager) { - super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback, - messageAreaControllerFactory, latencyTracker, liftToActivateListener); - mKeyguardUpdateMonitor = keyguardUpdateMonitor; - mTelephonyManager = telephonyManager; - mSimImageView = mView.findViewById(R.id.keyguard_sim); - } - - @Override - protected void onViewAttached() { - super.onViewAttached(); - mKeyguardUpdateMonitor.registerCallback(mUpdateMonitorCallback); - } - - @Override - protected void onViewDetached() { - super.onViewDetached(); - mKeyguardUpdateMonitor.removeCallback(mUpdateMonitorCallback); - } - - @Override - void resetState() { - super.resetState(); - mStateMachine.reset(); - } - - @Override - protected void verifyPasswordAndUnlock() { - mStateMachine.next(); - } - - private class StateMachine { - static final int ENTER_PUK = 0; - static final int ENTER_PIN = 1; - static final int CONFIRM_PIN = 2; - static final int DONE = 3; - - private int mState = ENTER_PUK; - - public void next() { - int msg = 0; - if (mState == ENTER_PUK) { - if (checkPuk()) { - mState = ENTER_PIN; - msg = com.android.systemui.R.string.kg_puk_enter_pin_hint; - } else { - msg = com.android.systemui.R.string.kg_invalid_sim_puk_hint; - } - } else if (mState == ENTER_PIN) { - if (checkPin()) { - mState = CONFIRM_PIN; - msg = com.android.systemui.R.string.kg_enter_confirm_pin_hint; - } else { - msg = com.android.systemui.R.string.kg_invalid_sim_pin_hint; - } - } else if (mState == CONFIRM_PIN) { - if (confirmPin()) { - mState = DONE; - msg = com.android.systemui.R.string.keyguard_sim_unlock_progress_dialog_message; - updateSim(); - } else { - mState = ENTER_PIN; // try again? - msg = com.android.systemui.R.string.kg_invalid_confirm_pin_hint; - } - } - mView.resetPasswordText(true /* animate */, true /* announce */); - if (msg != 0) { - mMessageAreaController.setMessage(msg); - } - } - - - void reset() { - mPinText = ""; - mPukText = ""; - mState = ENTER_PUK; - handleSubInfoChangeIfNeeded(); - if (mShowDefaultMessage) { - showDefaultMessage(); - } - boolean isEsimLocked = KeyguardEsimArea.isEsimLocked(mView.getContext(), mSubId); - - KeyguardEsimArea esimButton = mView.findViewById(R.id.keyguard_esim_area); - esimButton.setVisibility(isEsimLocked ? View.VISIBLE : View.GONE); - mPasswordEntry.requestFocus(); - } - } - - private void showDefaultMessage() { - if (mRemainingAttempts >= 0) { - mMessageAreaController.setMessage(mView.getPukPasswordErrorMessage( - mRemainingAttempts, true, - KeyguardEsimArea.isEsimLocked(mView.getContext(), mSubId))); - return; - } - - boolean isEsimLocked = KeyguardEsimArea.isEsimLocked(mView.getContext(), mSubId); - int count = 1; - if (mTelephonyManager != null) { - count = mTelephonyManager.getActiveModemCount(); - } - Resources rez = mView.getResources(); - String msg; - TypedArray array = mView.getContext().obtainStyledAttributes( - new int[] { R.attr.wallpaperTextColor }); - int color = array.getColor(0, Color.WHITE); - array.recycle(); - if (count < 2) { - msg = rez.getString(R.string.kg_puk_enter_puk_hint); - } else { - SubscriptionInfo info = Dependency.get(KeyguardUpdateMonitor.class) - .getSubscriptionInfoForSubId(mSubId); - CharSequence displayName = info != null ? info.getDisplayName() : ""; - msg = rez.getString(R.string.kg_puk_enter_puk_hint_multi, displayName); - if (info != null) { - color = info.getIconTint(); - } - } - if (isEsimLocked) { - msg = rez.getString(R.string.kg_sim_lock_esim_instructions, msg); - } - mMessageAreaController.setMessage(msg); - mSimImageView.setImageTintList(ColorStateList.valueOf(color)); - - // Sending empty PUK here to query the number of remaining PIN attempts - new CheckSimPuk("", "", mSubId) { - void onSimLockChangedResponse(final PinResult result) { - if (result == null) Log.e(TAG, "onSimCheckResponse, pin result is NULL"); - else { - Log.d(TAG, "onSimCheckResponse " + " empty One result " - + result.toString()); - if (result.getAttemptsRemaining() >= 0) { - mRemainingAttempts = result.getAttemptsRemaining(); - mMessageAreaController.setMessage( - mView.getPukPasswordErrorMessage( - result.getAttemptsRemaining(), true, - KeyguardEsimArea.isEsimLocked(mView.getContext(), mSubId))); - } - } - } - }.start(); - } - - private boolean checkPuk() { - // make sure the puk is at least 8 digits long. - if (mPasswordEntry.getText().length() == 8) { - mPukText = mPasswordEntry.getText(); - return true; - } - return false; - } - - private boolean checkPin() { - // make sure the PIN is between 4 and 8 digits - int length = mPasswordEntry.getText().length(); - if (length >= 4 && length <= 8) { - mPinText = mPasswordEntry.getText(); - return true; - } - return false; - } - - public boolean confirmPin() { - return mPinText.equals(mPasswordEntry.getText()); - } - - - - - private void updateSim() { - getSimUnlockProgressDialog().show(); - - if (mCheckSimPukThread == null) { - mCheckSimPukThread = new CheckSimPuk(mPukText, mPinText, mSubId) { - @Override - void onSimLockChangedResponse(final PinResult result) { - mView.post(() -> { - if (mSimUnlockProgressDialog != null) { - mSimUnlockProgressDialog.hide(); - } - mView.resetPasswordText(true /* animate */, - /* announce */ - result.getType() != PinResult.PIN_RESULT_TYPE_SUCCESS); - if (result.getType() == PinResult.PIN_RESULT_TYPE_SUCCESS) { - mKeyguardUpdateMonitor.reportSimUnlocked(mSubId); - mRemainingAttempts = -1; - mShowDefaultMessage = true; - - getKeyguardSecurityCallback().dismiss( - true, KeyguardUpdateMonitor.getCurrentUser()); - } else { - mShowDefaultMessage = false; - if (result.getType() == PinResult.PIN_RESULT_TYPE_INCORRECT) { - // show message - mMessageAreaController.setMessage(mView.getPukPasswordErrorMessage( - result.getAttemptsRemaining(), false, - KeyguardEsimArea.isEsimLocked(mView.getContext(), mSubId))); - if (result.getAttemptsRemaining() <= 2) { - // this is getting critical - show dialog - getPukRemainingAttemptsDialog( - result.getAttemptsRemaining()).show(); - } else { - // show message - mMessageAreaController.setMessage( - mView.getPukPasswordErrorMessage( - result.getAttemptsRemaining(), false, - KeyguardEsimArea.isEsimLocked( - mView.getContext(), mSubId))); - } - } else { - mMessageAreaController.setMessage(mView.getResources().getString( - R.string.kg_password_puk_failed)); - } - if (DEBUG) { - Log.d(TAG, "verifyPasswordAndUnlock " - + " UpdateSim.onSimCheckResponse: " - + " attemptsRemaining=" + result.getAttemptsRemaining()); - } - } - mStateMachine.reset(); - mCheckSimPukThread = null; - }); - } - }; - mCheckSimPukThread.start(); - } - } - - @Override - protected boolean shouldLockout(long deadline) { - // SIM PUK doesn't have a timed lockout - return false; - } - - private Dialog getSimUnlockProgressDialog() { - if (mSimUnlockProgressDialog == null) { - mSimUnlockProgressDialog = new ProgressDialog(mView.getContext()); - mSimUnlockProgressDialog.setMessage( - mView.getResources().getString(R.string.kg_sim_unlock_progress_dialog_message)); - mSimUnlockProgressDialog.setIndeterminate(true); - mSimUnlockProgressDialog.setCancelable(false); - if (!(mView.getContext() instanceof Activity)) { - mSimUnlockProgressDialog.getWindow().setType( - WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); - } - } - return mSimUnlockProgressDialog; - } - - private void handleSubInfoChangeIfNeeded() { - int subId = mKeyguardUpdateMonitor.getNextSubIdForState( - TelephonyManager.SIM_STATE_PUK_REQUIRED); - if (subId != mSubId && SubscriptionManager.isValidSubscriptionId(subId)) { - mSubId = subId; - mShowDefaultMessage = true; - mRemainingAttempts = -1; - } - } - - - private Dialog getPukRemainingAttemptsDialog(int remaining) { - String msg = mView.getPukPasswordErrorMessage(remaining, false, - KeyguardEsimArea.isEsimLocked(mView.getContext(), mSubId)); - if (mRemainingAttemptsDialog == null) { - AlertDialog.Builder builder = new AlertDialog.Builder(mView.getContext()); - builder.setMessage(msg); - builder.setCancelable(false); - builder.setNeutralButton(R.string.ok, null); - mRemainingAttemptsDialog = builder.create(); - mRemainingAttemptsDialog.getWindow().setType( - WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); - } else { - mRemainingAttemptsDialog.setMessage(msg); - } - return mRemainingAttemptsDialog; - } - - @Override - public void onPause() { - // dismiss the dialog. - if (mSimUnlockProgressDialog != null) { - mSimUnlockProgressDialog.dismiss(); - mSimUnlockProgressDialog = null; - } - } - - /** - * Since the IPC can block, we want to run the request in a separate thread - * with a callback. - */ - private abstract class CheckSimPuk extends Thread { - - private final String mPin, mPuk; - private final int mSubId; - - protected CheckSimPuk(String puk, String pin, int subId) { - mPuk = puk; - mPin = pin; - mSubId = subId; - } - - abstract void onSimLockChangedResponse(@NonNull PinResult result); - - @Override - public void run() { - if (DEBUG) Log.v(TAG, "call supplyPukReportResult()"); - TelephonyManager telephonyManager = mTelephonyManager.createForSubscriptionId(mSubId); - final PinResult result = telephonyManager.supplyPukReportPinResult(mPuk, mPin); - if (result == null) { - Log.e(TAG, "Error result for supplyPukReportResult."); - mView.post(() -> onSimLockChangedResponse(PinResult.getDefaultFailedResult())); - } else { - if (DEBUG) { - Log.v(TAG, "supplyPukReportResult returned: " + result.toString()); - } - mView.post(new Runnable() { - @Override - public void run() { - onSimLockChangedResponse(result); - } - }); - } - } - } - -} diff --git a/packages/SystemUI/src/com/android/keyguard/LiftToActivateListener.java b/packages/SystemUI/src/com/android/keyguard/LiftToActivateListener.java index 425e50ed6397..e59602b1cfff 100644 --- a/packages/SystemUI/src/com/android/keyguard/LiftToActivateListener.java +++ b/packages/SystemUI/src/com/android/keyguard/LiftToActivateListener.java @@ -16,12 +16,11 @@ package com.android.keyguard; +import android.content.Context; import android.view.MotionEvent; import android.view.View; import android.view.accessibility.AccessibilityManager; -import javax.inject.Inject; - /** * Hover listener that implements lift-to-activate interaction for * accessibility. May be added to multiple views. @@ -32,9 +31,9 @@ class LiftToActivateListener implements View.OnHoverListener { private boolean mCachedClickableState; - @Inject - LiftToActivateListener(AccessibilityManager accessibilityManager) { - mAccessibilityManager = accessibilityManager; + public LiftToActivateListener(Context context) { + mAccessibilityManager = (AccessibilityManager) context.getSystemService( + Context.ACCESSIBILITY_SERVICE); } @Override diff --git a/packages/SystemUI/src/com/android/keyguard/NumPadKey.java b/packages/SystemUI/src/com/android/keyguard/NumPadKey.java index 2205fdd4267d..b0457fce6a1a 100644 --- a/packages/SystemUI/src/com/android/keyguard/NumPadKey.java +++ b/packages/SystemUI/src/com/android/keyguard/NumPadKey.java @@ -26,7 +26,6 @@ import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; -import android.view.accessibility.AccessibilityManager; import android.widget.TextView; import com.android.internal.widget.LockPatternUtils; @@ -91,8 +90,7 @@ public class NumPadKey extends ViewGroup { } setOnClickListener(mListener); - setOnHoverListener(new LiftToActivateListener( - (AccessibilityManager) context.getSystemService(Context.ACCESSIBILITY_SERVICE))); + setOnHoverListener(new LiftToActivateListener(context)); mLockPatternUtils = new LockPatternUtils(context); mPM = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerModule.java b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerModule.java index 881108858b51..b6010c8915e7 100644 --- a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerModule.java +++ b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerModule.java @@ -22,7 +22,6 @@ import android.view.ViewGroup; import com.android.keyguard.KeyguardHostView; import com.android.keyguard.KeyguardMessageArea; import com.android.keyguard.KeyguardSecurityContainer; -import com.android.keyguard.KeyguardSecurityViewFlipper; import com.android.systemui.R; import com.android.systemui.statusbar.phone.KeyguardBouncer; @@ -59,15 +58,7 @@ public interface KeyguardBouncerModule { /** */ @Provides @KeyguardBouncerScope - static KeyguardSecurityContainer providesKeyguardSecurityContainer(KeyguardHostView hostView) { + static KeyguardSecurityContainer preovidesKeyguardSecurityContainer(KeyguardHostView hostView) { return hostView.findViewById(R.id.keyguard_security_container); } - - /** */ - @Provides - @KeyguardBouncerScope - static KeyguardSecurityViewFlipper providesKeyguardSecurityViewFlipper( - KeyguardSecurityContainer containerView) { - return containerView.findViewById(R.id.view_flipper); - } } diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleReminderExpBehavior.java b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleReminderExpBehavior.java index 1b2e4c6a595e..02a672b587d8 100644 --- a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleReminderExpBehavior.java +++ b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleReminderExpBehavior.java @@ -26,6 +26,8 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.ResolveInfo; +import android.database.ContentObserver; +import android.net.Uri; import android.os.Handler; import android.provider.Settings; @@ -66,8 +68,10 @@ import dagger.Lazy; @SysUISingleton final class AssistHandleReminderExpBehavior implements BehaviorController { - private static final String LEARNING_TIME_ELAPSED_KEY = "reminder_exp_learning_time_elapsed"; - private static final String LEARNING_EVENT_COUNT_KEY = "reminder_exp_learning_event_count"; + private static final Uri LEARNING_TIME_ELAPSED_URI = + Settings.Secure.getUriFor(Settings.Secure.ASSIST_HANDLES_LEARNING_TIME_ELAPSED_MILLIS); + private static final Uri LEARNING_EVENT_COUNT_URI = + Settings.Secure.getUriFor(Settings.Secure.ASSIST_HANDLES_LEARNING_EVENT_COUNT); private static final String LEARNED_HINT_LAST_SHOWN_KEY = "reminder_exp_learned_hint_last_shown"; private static final long DEFAULT_LEARNING_TIME_MS = TimeUnit.DAYS.toMillis(10); @@ -181,6 +185,7 @@ final class AssistHandleReminderExpBehavior implements BehaviorController { private boolean mIsNavBarHidden; private boolean mIsLauncherShowing; private int mConsecutiveTaskSwitches; + @Nullable private ContentObserver mSettingObserver; /** Whether user has learned the gesture. */ private boolean mIsLearned; @@ -248,9 +253,22 @@ final class AssistHandleReminderExpBehavior implements BehaviorController { mWakefulnessLifecycle.get().addObserver(mWakefulnessLifecycleObserver); mLearningTimeElapsed = Settings.Secure.getLong( - context.getContentResolver(), LEARNING_TIME_ELAPSED_KEY, /* default = */ 0); + context.getContentResolver(), + Settings.Secure.ASSIST_HANDLES_LEARNING_TIME_ELAPSED_MILLIS, + /* default = */ 0); mLearningCount = Settings.Secure.getInt( - context.getContentResolver(), LEARNING_EVENT_COUNT_KEY, /* default = */ 0); + context.getContentResolver(), + Settings.Secure.ASSIST_HANDLES_LEARNING_EVENT_COUNT, + /* default = */ 0); + mSettingObserver = new SettingsObserver(context, mHandler); + context.getContentResolver().registerContentObserver( + LEARNING_TIME_ELAPSED_URI, + /* notifyForDescendants = */ true, + mSettingObserver); + context.getContentResolver().registerContentObserver( + LEARNING_EVENT_COUNT_URI, + /* notifyForDescendants = */ true, + mSettingObserver); mLearnedHintLastShownEpochDay = Settings.Secure.getLong( context.getContentResolver(), LEARNED_HINT_LAST_SHOWN_KEY, /* default = */ 0); mLastLearningTimestamp = mClock.currentTimeMillis(); @@ -264,8 +282,20 @@ final class AssistHandleReminderExpBehavior implements BehaviorController { if (mContext != null) { mBroadcastDispatcher.get().unregisterReceiver(mDefaultHomeBroadcastReceiver); mBootCompleteCache.get().removeListener(mBootCompleteListener); - Settings.Secure.putLong(mContext.getContentResolver(), LEARNING_TIME_ELAPSED_KEY, 0); - Settings.Secure.putInt(mContext.getContentResolver(), LEARNING_EVENT_COUNT_KEY, 0); + mContext.getContentResolver().unregisterContentObserver(mSettingObserver); + mSettingObserver = null; + // putString in order to use overrideableByRestore + Settings.Secure.putString( + mContext.getContentResolver(), + Settings.Secure.ASSIST_HANDLES_LEARNING_TIME_ELAPSED_MILLIS, + Long.toString(0L), + /* overrideableByRestore = */ true); + // putString in order to use overrideableByRestore + Settings.Secure.putString( + mContext.getContentResolver(), + Settings.Secure.ASSIST_HANDLES_LEARNING_EVENT_COUNT, + Integer.toString(0), + /* overrideableByRestore = */ true); Settings.Secure.putLong(mContext.getContentResolver(), LEARNED_HINT_LAST_SHOWN_KEY, 0); mContext = null; } @@ -282,8 +312,12 @@ final class AssistHandleReminderExpBehavior implements BehaviorController { return; } - Settings.Secure.putLong( - mContext.getContentResolver(), LEARNING_EVENT_COUNT_KEY, ++mLearningCount); + // putString in order to use overrideableByRestore + Settings.Secure.putString( + mContext.getContentResolver(), + Settings.Secure.ASSIST_HANDLES_LEARNING_EVENT_COUNT, + Integer.toString(++mLearningCount), + /* overrideableByRestore = */ true); } @Override @@ -460,8 +494,12 @@ final class AssistHandleReminderExpBehavior implements BehaviorController { mIsLearned = mLearningCount >= getLearningCount() || mLearningTimeElapsed >= getLearningTimeMs(); - mHandler.post(() -> Settings.Secure.putLong( - mContext.getContentResolver(), LEARNING_TIME_ELAPSED_KEY, mLearningTimeElapsed)); + // putString in order to use overrideableByRestore + mHandler.post(() -> Settings.Secure.putString( + mContext.getContentResolver(), + Settings.Secure.ASSIST_HANDLES_LEARNING_TIME_ELAPSED_MILLIS, + Long.toString(mLearningTimeElapsed), + /* overrideableByRestore = */ true)); } private void resetConsecutiveTaskSwitches() { @@ -589,4 +627,32 @@ final class AssistHandleReminderExpBehavior implements BehaviorController { + "=" + getShowWhenTaught()); } + + private final class SettingsObserver extends ContentObserver { + + private final Context mContext; + + SettingsObserver(Context context, Handler handler) { + super(handler); + mContext = context; + } + + @Override + public void onChange(boolean selfChange, @Nullable Uri uri) { + if (LEARNING_TIME_ELAPSED_URI.equals(uri)) { + mLastLearningTimestamp = mClock.currentTimeMillis(); + mLearningTimeElapsed = Settings.Secure.getLong( + mContext.getContentResolver(), + Settings.Secure.ASSIST_HANDLES_LEARNING_TIME_ELAPSED_MILLIS, + /* default = */ 0); + } else if (LEARNING_EVENT_COUNT_URI.equals(uri)) { + mLearningCount = Settings.Secure.getInt( + mContext.getContentResolver(), + Settings.Secure.ASSIST_HANDLES_LEARNING_EVENT_COUNT, + /* default = */ 0); + } + + super.onChange(selfChange, uri); + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java index eb431274b8a3..38e12a6ed5f8 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java @@ -288,7 +288,6 @@ public class DependencyProvider { /** */ @Provides - @SysUISingleton public LockPatternUtils provideLockPatternUtils(Context context) { return new LockPatternUtils(context); } diff --git a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java index 79925bad3cc7..b35579d3624b 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java @@ -62,7 +62,6 @@ import android.view.ViewConfiguration; import android.view.WindowManager; import android.view.WindowManagerGlobal; import android.view.accessibility.AccessibilityManager; -import android.view.inputmethod.InputMethodManager; import com.android.internal.app.IBatteryStats; import com.android.internal.statusbar.IStatusBarService; @@ -184,12 +183,6 @@ public class FrameworkServicesModule { @Provides @Singleton - static InputMethodManager provideInputMethodManager(Context context) { - return context.getSystemService(InputMethodManager.class); - } - - @Provides - @Singleton static IPackageManager provideIPackageManager() { return IPackageManager.Stub.asInterface(ServiceManager.getService("package")); } diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt index f150381f4070..636f42089743 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt @@ -274,7 +274,7 @@ class MediaCarouselController @Inject constructor( } } - private fun removePlayer(key: String) { + private fun removePlayer(key: String, dismissMediaData: Boolean = true) { val removed = MediaPlayerData.removeMediaPlayer(key) removed?.apply { mediaCarouselScrollHandler.onPrePlayerRemoved(removed) @@ -283,13 +283,16 @@ class MediaCarouselController @Inject constructor( mediaCarouselScrollHandler.onPlayersChanged() updatePageIndicator() - // Inform the media manager of a potentially late dismissal - mediaManager.dismissMediaData(key, 0L) + if (dismissMediaData) { + // Inform the media manager of a potentially late dismissal + mediaManager.dismissMediaData(key, 0L) + } } } private fun recreatePlayers() { MediaPlayerData.mediaData().forEach { (key, data) -> + removePlayer(key, dismissMediaData = false) addOrUpdatePlayer(key = key, oldKey = null, data = data) } } 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 9fc64d51cdf7..9b6a9ea80ebe 100644 --- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java +++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java @@ -42,7 +42,7 @@ import java.util.List; public class MediaOutputAdapter extends MediaOutputBaseAdapter { private static final String TAG = "MediaOutputAdapter"; - private static final int PAIR_NEW = 1; + private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); public MediaOutputAdapter(MediaOutputController controller) { super(controller); @@ -58,11 +58,14 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter { @Override public void onBindViewHolder(@NonNull MediaDeviceBaseViewHolder viewHolder, int position) { - if (mController.isZeroMode() && position == (mController.getMediaDevices().size())) { - viewHolder.onBind(PAIR_NEW); - } else if (position < (mController.getMediaDevices().size())) { - viewHolder.onBind(((List<MediaDevice>) (mController.getMediaDevices())).get(position)); - } else { + final int size = mController.getMediaDevices().size(); + if (mController.isZeroMode() && position == size) { + viewHolder.onBind(CUSTOMIZED_ITEM_PAIR_NEW, false /* topMargin */, + true /* bottomMargin */); + } else if (position < size) { + viewHolder.onBind(((List<MediaDevice>) (mController.getMediaDevices())).get(position), + position == 0 /* topMargin */, position == (size - 1) /* bottomMargin */); + } else if (DEBUG) { Log.d(TAG, "Incorrect position: " + position); } } @@ -83,7 +86,7 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter { } void onItemClick(int customizedItem) { - if (customizedItem == PAIR_NEW) { + if (customizedItem == CUSTOMIZED_ITEM_PAIR_NEW) { mController.launchBluetoothPairing(); } } @@ -112,51 +115,49 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter { } @Override - void onBind(MediaDevice device) { - super.onBind(device); + void onBind(MediaDevice device, boolean topMargin, boolean bottomMargin) { + super.onBind(device, topMargin, bottomMargin); if (mController.isTransferring()) { if (device.getState() == MediaDeviceState.STATE_CONNECTING && !mController.hasAdjustVolumeUserRestriction()) { - setTwoLineLayout(device, true); - mProgressBar.setVisibility(View.VISIBLE); - mSeekBar.setVisibility(View.GONE); - mSubTitleText.setVisibility(View.GONE); + setTwoLineLayout(device, null /* title */, true /* bFocused */, + false /* showSeekBar*/, true /* showProgressBar */, + false /* showSubtitle */); } else { - setSingleLineLayout(getItemTitle(device), false); + setSingleLineLayout(getItemTitle(device), false /* bFocused */); } } else { // Set different layout for each device if (device.getState() == MediaDeviceState.STATE_CONNECTING_FAILED) { - setTwoLineLayout(device, false); - mSubTitleText.setVisibility(View.VISIBLE); - mSeekBar.setVisibility(View.GONE); - mProgressBar.setVisibility(View.GONE); + setTwoLineLayout(device, null /* title */, false /* bFocused */, + false /* showSeekBar*/, false /* showProgressBar */, + true /* showSubtitle */); mSubTitleText.setText(R.string.media_output_dialog_connect_failed); mFrameLayout.setOnClickListener(v -> onItemClick(device)); } else if (!mController.hasAdjustVolumeUserRestriction() && isCurrentConnected(device)) { - setTwoLineLayout(device, true); - mSeekBar.setVisibility(View.VISIBLE); - mProgressBar.setVisibility(View.GONE); - mSubTitleText.setVisibility(View.GONE); + setTwoLineLayout(device, null /* title */, true /* bFocused */, + true /* showSeekBar*/, false /* showProgressBar */, + false /* showSubtitle */); initSeekbar(device); } else { - setSingleLineLayout(getItemTitle(device), false); + setSingleLineLayout(getItemTitle(device), false /* bFocused */); mFrameLayout.setOnClickListener(v -> onItemClick(device)); } } } @Override - void onBind(int customizedItem) { - if (customizedItem == PAIR_NEW) { + void onBind(int customizedItem, boolean topMargin, boolean bottomMargin) { + super.onBind(customizedItem, topMargin, bottomMargin); + if (customizedItem == CUSTOMIZED_ITEM_PAIR_NEW) { setSingleLineLayout(mContext.getText(R.string.media_output_dialog_pairing_new), - false); + false /* bFocused */); final Drawable d = mContext.getDrawable(R.drawable.ic_add); d.setColorFilter(new PorterDuffColorFilter( Utils.getColorAccentDefaultColor(mContext), PorterDuff.Mode.SRC_IN)); mTitleIcon.setImageDrawable(d); - mFrameLayout.setOnClickListener(v -> onItemClick(PAIR_NEW)); + mFrameLayout.setOnClickListener(v -> onItemClick(CUSTOMIZED_ITEM_PAIR_NEW)); } } } 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 7579c25b030a..01dc6c4b71da 100644 --- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java +++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java @@ -44,9 +44,12 @@ public abstract class MediaOutputBaseAdapter extends private static final String FONT_SELECTED_TITLE = "sans-serif-medium"; private static final String FONT_TITLE = "sans-serif"; + static final int CUSTOMIZED_ITEM_PAIR_NEW = 1; + final MediaOutputController mController; private boolean mIsDragging; + private int mMargin; Context mContext; View mHolderView; @@ -60,6 +63,8 @@ public abstract class MediaOutputBaseAdapter extends public MediaDeviceBaseViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int viewType) { mContext = viewGroup.getContext(); + mMargin = mContext.getResources().getDimensionPixelSize( + R.dimen.media_output_dialog_list_margin); mHolderView = LayoutInflater.from(mContext).inflate(R.layout.media_output_list_item, viewGroup, false); @@ -106,12 +111,26 @@ public abstract class MediaOutputBaseAdapter extends mSeekBar = view.requireViewById(R.id.volume_seekbar); } - void onBind(MediaDevice device) { + void onBind(MediaDevice device, boolean topMargin, boolean bottomMargin) { mTitleIcon.setImageIcon(mController.getDeviceIconCompat(device).toIcon(mContext)); + setMargin(topMargin, bottomMargin); } - void onBind(int customizedItem) { } + void onBind(int customizedItem, boolean topMargin, boolean bottomMargin) { + setMargin(topMargin, bottomMargin); + } + private void setMargin(boolean topMargin, boolean bottomMargin) { + ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) mFrameLayout + .getLayoutParams(); + if (topMargin) { + params.topMargin = mMargin; + } + if (bottomMargin) { + params.bottomMargin = mMargin; + } + mFrameLayout.setLayoutParams(params); + } void setSingleLineLayout(CharSequence title, boolean bFocused) { mTitleText.setVisibility(View.VISIBLE); mTwoLineLayout.setVisibility(View.GONE); @@ -123,10 +142,19 @@ public abstract class MediaOutputBaseAdapter extends } } - void setTwoLineLayout(MediaDevice device, boolean bFocused) { + void setTwoLineLayout(MediaDevice device, CharSequence title, boolean bFocused, + boolean showSeekBar, boolean showProgressBar, boolean showSubtitle) { mTitleText.setVisibility(View.GONE); mTwoLineLayout.setVisibility(View.VISIBLE); - mTwoLineTitleText.setText(getItemTitle(device)); + mSeekBar.setVisibility(showSeekBar ? View.VISIBLE : View.GONE); + mProgressBar.setVisibility(showProgressBar ? View.VISIBLE : View.GONE); + mSubTitleText.setVisibility(showSubtitle ? View.VISIBLE : View.GONE); + if (device == null) { + mTwoLineTitleText.setText(title); + } else { + mTwoLineTitleText.setText(getItemTitle(device)); + } + if (bFocused) { mTwoLineTitleText.setTypeface(Typeface.create(FONT_SELECTED_TITLE, Typeface.NORMAL)); 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 f8f4f4df58bc..ebca8a735ad5 100644 --- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java +++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java @@ -33,7 +33,6 @@ import android.view.Window; import android.view.WindowInsets; import android.view.WindowManager; import android.widget.Button; -import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; @@ -69,12 +68,9 @@ public abstract class MediaOutputBaseDialog extends SystemUIDialog implements private LinearLayout mDeviceListLayout; private Button mDoneButton; private Button mStopButton; - private View mListBottomPadding; private int mListMaxHeight; MediaOutputBaseAdapter mAdapter; - FrameLayout mGroupItemController; - View mGroupDivider; private final ViewTreeObserver.OnGlobalLayoutListener mDeviceListLayoutListener = () -> { // Set max height for list @@ -114,12 +110,9 @@ public abstract class MediaOutputBaseDialog extends SystemUIDialog implements mHeaderSubtitle = mDialogView.requireViewById(R.id.header_subtitle); mHeaderIcon = mDialogView.requireViewById(R.id.header_icon); mDevicesRecyclerView = mDialogView.requireViewById(R.id.list_result); - mGroupItemController = mDialogView.requireViewById(R.id.group_item_controller); - mGroupDivider = mDialogView.requireViewById(R.id.group_item_divider); mDeviceListLayout = mDialogView.requireViewById(R.id.device_list); mDoneButton = mDialogView.requireViewById(R.id.done); mStopButton = mDialogView.requireViewById(R.id.stop); - mListBottomPadding = mDialogView.requireViewById(R.id.list_bottom_padding); mDeviceListLayout.getViewTreeObserver().addOnGlobalLayoutListener( mDeviceListLayoutListener); @@ -162,7 +155,9 @@ public abstract class MediaOutputBaseDialog extends SystemUIDialog implements } if (mHeaderIcon.getVisibility() == View.VISIBLE) { final int size = getHeaderIconSize(); - mHeaderIcon.setLayoutParams(new LinearLayout.LayoutParams(size, size)); + final int padding = mContext.getResources().getDimensionPixelSize( + R.dimen.media_output_dialog_header_icon_padding); + mHeaderIcon.setLayoutParams(new LinearLayout.LayoutParams(size + padding, size)); } // Update title and subtitle mHeaderTitle.setText(getHeaderText()); @@ -178,12 +173,8 @@ public abstract class MediaOutputBaseDialog extends SystemUIDialog implements if (!mAdapter.isDragging()) { mAdapter.notifyDataSetChanged(); } - // Add extra padding when device amount is less than 6 - if (mMediaOutputController.getMediaDevices().size() < 6) { - mListBottomPadding.setVisibility(View.VISIBLE); - } else { - mListBottomPadding.setVisibility(View.GONE); - } + // Show when remote media session is available + mStopButton.setVisibility(getStopButtonVisibility()); } abstract int getHeaderIconRes(); @@ -196,6 +187,8 @@ public abstract class MediaOutputBaseDialog extends SystemUIDialog implements abstract CharSequence getHeaderSubtitle(); + abstract int getStopButtonVisibility(); + @Override public void onMediaChanged() { mMainThreadHandler.post(() -> refresh()); 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 64d20a273931..b1f1bda25961 100644 --- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java +++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java @@ -24,6 +24,7 @@ import android.graphics.Canvas; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.media.MediaMetadata; +import android.media.MediaRoute2Info; import android.media.RoutingSessionInfo; import android.media.session.MediaController; import android.media.session.MediaSessionManager; @@ -63,7 +64,7 @@ import javax.inject.Inject; public class MediaOutputController implements LocalMediaManager.DeviceCallback{ private static final String TAG = "MediaOutputController"; - private static final boolean DEBUG = false; + private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); private final String mPackageName; private final Context mContext; @@ -406,6 +407,14 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback{ mActivityStarter.dismissKeyguardThenExecute(postKeyguardAction, null, true); } + boolean isActiveRemoteDevice(@NonNull MediaDevice device) { + final List<String> features = device.getFeatures(); + return (features.contains(MediaRoute2Info.FEATURE_REMOTE_PLAYBACK) + || features.contains(MediaRoute2Info.FEATURE_REMOTE_AUDIO_PLAYBACK) + || features.contains(MediaRoute2Info.FEATURE_REMOTE_VIDEO_PLAYBACK) + || features.contains(MediaRoute2Info.FEATURE_REMOTE_GROUP_PLAYBACK)); + } + private final MediaController.Callback mCb = new MediaController.Callback() { @Override public void onMetadataChanged(MediaMetadata metadata) { diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java index ac9d8ce52d88..a892a12f387b 100644 --- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java +++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java @@ -45,8 +45,6 @@ public class MediaOutputDialog extends MediaOutputBaseDialog { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - mGroupItemController.setVisibility(View.GONE); - mGroupDivider.setVisibility(View.GONE); } @Override @@ -74,4 +72,10 @@ public class MediaOutputDialog extends MediaOutputBaseDialog { CharSequence getHeaderSubtitle() { return mMediaOutputController.getHeaderSubTitle(); } + + @Override + int getStopButtonVisibility() { + return mMediaOutputController.isActiveRemoteDevice( + mMediaOutputController.getCurrentConnectedMediaDevice()) ? View.VISIBLE : View.GONE; + } } diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt index bc1dca58990d..4cdca4cbcf1e 100644 --- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt +++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt @@ -33,10 +33,22 @@ class MediaOutputDialogFactory @Inject constructor( private val shadeController: ShadeController, private val starter: ActivityStarter ) { + companion object { + var mediaOutputDialog: MediaOutputDialog? = null + } + /** Creates a [MediaOutputDialog] for the given package. */ fun create(packageName: String, aboveStatusBar: Boolean) { - MediaOutputController(context, packageName, mediaSessionManager, lbm, shadeController, - starter).run { + mediaOutputDialog?.dismiss() + + mediaOutputDialog = MediaOutputController(context, packageName, mediaSessionManager, lbm, + shadeController, starter).run { MediaOutputDialog(context, aboveStatusBar, this) } } + + /** dismiss [MediaOutputDialog] if exist. */ + fun dismiss() { + mediaOutputDialog?.dismiss() + mediaOutputDialog = null + } } diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java index 5b07db6c91a1..a3913aa9db93 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java @@ -254,7 +254,9 @@ public class PipMenuActivityController { if (isMenuVisible()) { // If the menu is visible in either the closed or full state, then hide the menu and // trigger the animation trigger afterwards - onStartCallback.run(); + if (onStartCallback != null) { + onStartCallback.run(); + } mPipMenuView.hideMenu(onEndCallback); } } diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuView.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuView.java index c66f442c4c0d..1c38ab338969 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuView.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuView.java @@ -316,7 +316,7 @@ public class PipMenuView extends FrameLayout { } void hideMenu(Runnable animationEndCallback) { - hideMenu(animationEndCallback, true /* notifyMenuVisibility */, false); + hideMenu(animationEndCallback, true /* notifyMenuVisibility */, true /* animate */); } private void hideMenu(final Runnable animationFinishedRunnable, boolean notifyMenuVisibility, @@ -394,8 +394,10 @@ public class PipMenuView extends FrameLayout { // TODO: Check if the action drawable has changed before we reload it action.getIcon().loadDrawableAsync(mContext, d -> { - d.setTint(Color.WHITE); - actionView.setImageDrawable(d); + if (d != null) { + d.setTint(Color.WHITE); + actionView.setImageDrawable(d); + } }, mHandler); actionView.setContentDescription(action.getContentDescription()); if (action.isEnabled()) { diff --git a/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java b/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java index a6cd350b33ce..eb8f065149c8 100644 --- a/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java +++ b/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java @@ -23,6 +23,7 @@ import android.view.InflateException; import android.view.LayoutInflater; import android.view.View; +import com.android.keyguard.KeyguardMessageArea; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.qs.QSFooterImpl; import com.android.systemui.qs.QSPanel; @@ -107,6 +108,11 @@ public class InjectionInflationController { NotificationStackScrollLayout createNotificationStackScrollLayout(); /** + * Creates the KeyguardMessageArea. + */ + KeyguardMessageArea createKeyguardMessageArea(); + + /** * Creates the QSPanel. */ QSPanel createQSPanel(); diff --git a/packages/SystemUI/src/com/android/systemui/util/ViewController.java b/packages/SystemUI/src/com/android/systemui/util/ViewController.java index c7aa780fcacb..64f8dbbb9e34 100644 --- a/packages/SystemUI/src/com/android/systemui/util/ViewController.java +++ b/packages/SystemUI/src/com/android/systemui/util/ViewController.java @@ -23,20 +23,7 @@ import android.view.View.OnAttachStateChangeListener; * Utility class that handles view lifecycle events for View Controllers. * * Implementations should handle setup and teardown related activities inside of - * {@link #onViewAttached()} and {@link #onViewDetached()}. Be sure to call {@link #init()} on - * any child controllers that this uses. This can be done in {@link init()} if the controllers - * are injected, or right after creation time of the child controller. - * - * Tip: View "attachment" happens top down - parents are notified that they are attached before - * any children. That means that if you call a method on a child controller in - * {@link #onViewAttached()}, the child controller may not have had its onViewAttach method - * called, so it may not be fully set up. - * - * As such, make sure that methods on your controller are safe to call _before_ its {@link #init()} - * and {@link #onViewAttached()} methods are called. Specifically, if your controller must call - * {@link View#findViewById(int)} on its root view to setup member variables, do so in its - * constructor. Save {@link #onViewAttached()} for things that can happen post-construction - adding - * listeners, dynamically changing content, or other runtime decisions. + * {@link #onViewAttached()} and {@link #onViewDetached()}. * * @param <T> View class that this ViewController is for. */ @@ -67,12 +54,10 @@ public abstract class ViewController<T extends View> { } mInited = true; - if (mView != null) { - if (mView.isAttachedToWindow()) { - mOnAttachStateListener.onViewAttachedToWindow(mView); - } - mView.addOnAttachStateChangeListener(mOnAttachStateListener); + if (mView.isAttachedToWindow()) { + mOnAttachStateListener.onViewAttachedToWindow(mView); } + mView.addOnAttachStateChangeListener(mOnAttachStateListener); } /** diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java index a2d6ac8f8511..a3512bec6605 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java @@ -86,6 +86,7 @@ public final class WMShell extends SystemUI private final ProtoTracer mProtoTracer; private KeyguardUpdateMonitorCallback mSplitScreenKeyguardCallback; + private KeyguardUpdateMonitorCallback mPipKeyguardCallback; private KeyguardUpdateMonitorCallback mOneHandedKeyguardCallback; @Inject @@ -140,6 +141,15 @@ public final class WMShell extends SystemUI pip.showPictureInPictureMenu(); } }); + mPipKeyguardCallback = new KeyguardUpdateMonitorCallback() { + @Override + public void onKeyguardVisibilityChanged(boolean showing) { + if (showing) { + pip.hidePipMenu(null, null); + } + } + }; + mKeyguardUpdateMonitor.registerCallback(mPipKeyguardCallback); } @VisibleForTesting diff --git a/packages/SystemUI/tests/src/com/android/keyguard/AdminSecondaryLockScreenControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/AdminSecondaryLockScreenControllerTest.java index dffad6ccbea5..9be2d124026c 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/AdminSecondaryLockScreenControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/AdminSecondaryLockScreenControllerTest.java @@ -41,6 +41,8 @@ import android.testing.TestableLooper.RunWithLooper; import android.testing.ViewUtils; import android.view.SurfaceControlViewHost; import android.view.SurfaceView; +import android.view.ViewGroup; +import android.widget.FrameLayout; import androidx.test.filters.SmallTest; @@ -65,7 +67,7 @@ public class AdminSecondaryLockScreenControllerTest extends SysuiTestCase { private ComponentName mComponentName; private Intent mServiceIntent; private TestableLooper mTestableLooper; - private KeyguardSecurityContainer mKeyguardSecurityContainer; + private ViewGroup mParent; @Mock private Handler mHandler; @@ -82,8 +84,8 @@ public class AdminSecondaryLockScreenControllerTest extends SysuiTestCase { public void setUp() { MockitoAnnotations.initMocks(this); - mKeyguardSecurityContainer = spy(new KeyguardSecurityContainer(mContext)); - ViewUtils.attachView(mKeyguardSecurityContainer); + mParent = spy(new FrameLayout(mContext)); + ViewUtils.attachView(mParent); mTestableLooper = TestableLooper.get(this); mComponentName = new ComponentName(mContext, "FakeKeyguardClient.class"); @@ -94,14 +96,13 @@ public class AdminSecondaryLockScreenControllerTest extends SysuiTestCase { when(mKeyguardClient.queryLocalInterface(anyString())).thenReturn(mKeyguardClient); when(mKeyguardClient.asBinder()).thenReturn(mKeyguardClient); - mTestController = new AdminSecondaryLockScreenController.Factory( - mContext, mKeyguardSecurityContainer, mUpdateMonitor, mHandler) - .create(mKeyguardCallback); + mTestController = new AdminSecondaryLockScreenController( + mContext, mParent, mUpdateMonitor, mKeyguardCallback, mHandler); } @After public void tearDown() { - ViewUtils.detachView(mKeyguardSecurityContainer); + ViewUtils.detachView(mParent); } @Test @@ -145,7 +146,7 @@ public class AdminSecondaryLockScreenControllerTest extends SysuiTestCase { SurfaceView v = verifySurfaceReady(); mTestController.hide(); - verify(mKeyguardSecurityContainer).removeView(v); + verify(mParent).removeView(v); assertThat(mContext.isBound(mComponentName)).isFalse(); } @@ -153,7 +154,7 @@ public class AdminSecondaryLockScreenControllerTest extends SysuiTestCase { public void testHide_notShown() throws Exception { mTestController.hide(); // Nothing should happen if trying to hide when the view isn't attached yet. - verify(mKeyguardSecurityContainer, never()).removeView(any(SurfaceView.class)); + verify(mParent, never()).removeView(any(SurfaceView.class)); } @Test @@ -181,7 +182,7 @@ public class AdminSecondaryLockScreenControllerTest extends SysuiTestCase { private SurfaceView verifySurfaceReady() throws Exception { mTestableLooper.processAllMessages(); ArgumentCaptor<SurfaceView> captor = ArgumentCaptor.forClass(SurfaceView.class); - verify(mKeyguardSecurityContainer).addView(captor.capture()); + verify(mParent).addView(captor.capture()); mTestableLooper.processAllMessages(); verify(mKeyguardClient).onCreateKeyguardSurface(any(), any(IKeyguardCallback.class)); @@ -189,7 +190,7 @@ public class AdminSecondaryLockScreenControllerTest extends SysuiTestCase { } private void verifyViewDismissed(SurfaceView v) throws Exception { - verify(mKeyguardSecurityContainer).removeView(v); + verify(mParent).removeView(v); verify(mKeyguardCallback).dismiss(true, TARGET_USER_ID, true); assertThat(mContext.isBound(mComponentName)).isFalse(); } diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java deleted file mode 100644 index c2ade81a9877..000000000000 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java +++ /dev/null @@ -1,122 +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.keyguard; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyZeroInteractions; -import static org.mockito.Mockito.when; - -import android.testing.AndroidTestingRunner; -import android.testing.TestableLooper.RunWithLooper; -import android.view.KeyEvent; - -import androidx.test.filters.SmallTest; - -import com.android.internal.util.LatencyTracker; -import com.android.internal.widget.LockPatternUtils; -import com.android.keyguard.KeyguardAbsKeyInputView.KeyDownListener; -import com.android.keyguard.KeyguardSecurityModel.SecurityMode; -import com.android.systemui.R; -import com.android.systemui.SysuiTestCase; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -@SmallTest -@RunWith(AndroidTestingRunner.class) -@RunWithLooper -public class KeyguardAbsKeyInputViewControllerTest extends SysuiTestCase { - - @Mock - private KeyguardAbsKeyInputView mAbsKeyInputView; - @Mock - private PasswordTextView mPasswordEntry; - @Mock - private KeyguardMessageArea mKeyguardMessageArea; - @Mock - private KeyguardUpdateMonitor mKeyguardUpdateMonitor; - @Mock - private SecurityMode mSecurityMode; - @Mock - private LockPatternUtils mLockPatternUtils; - @Mock - private KeyguardSecurityCallback mKeyguardSecurityCallback; - @Mock - private KeyguardMessageAreaController.Factory mKeyguardMessageAreaControllerFactory; - @Mock - private KeyguardMessageAreaController mKeyguardMessageAreaController; - @Mock - private LatencyTracker mLatencyTracker; - - private KeyguardAbsKeyInputViewController mKeyguardAbsKeyInputViewController; - - @Before - public void setup() { - MockitoAnnotations.initMocks(this); - when(mKeyguardMessageAreaControllerFactory.create(any(KeyguardMessageArea.class))) - .thenReturn(mKeyguardMessageAreaController); - when(mAbsKeyInputView.getPasswordTextViewId()).thenReturn(1); - when(mAbsKeyInputView.findViewById(1)).thenReturn(mPasswordEntry); - when(mAbsKeyInputView.isAttachedToWindow()).thenReturn(true); - when(mAbsKeyInputView.findViewById(R.id.keyguard_message_area)) - .thenReturn(mKeyguardMessageArea); - mKeyguardAbsKeyInputViewController = new KeyguardAbsKeyInputViewController(mAbsKeyInputView, - mKeyguardUpdateMonitor, mSecurityMode, mLockPatternUtils, mKeyguardSecurityCallback, - mKeyguardMessageAreaControllerFactory, mLatencyTracker) { - @Override - void resetState() { - } - - @Override - public void onResume(int reason) { - super.onResume(reason); - } - }; - mKeyguardAbsKeyInputViewController.init(); - reset(mKeyguardMessageAreaController); // Clear out implicit call to init. - } - - @Test - public void onKeyDown_clearsSecurityMessage() { - ArgumentCaptor<KeyDownListener> onKeyDownListenerArgumentCaptor = - ArgumentCaptor.forClass(KeyDownListener.class); - verify(mAbsKeyInputView).setKeyDownListener(onKeyDownListenerArgumentCaptor.capture()); - onKeyDownListenerArgumentCaptor.getValue().onKeyDown( - KeyEvent.KEYCODE_0, mock(KeyEvent.class)); - verify(mKeyguardSecurityCallback).userActivity(); - verify(mKeyguardMessageAreaController).setMessage(eq("")); - } - - @Test - public void onKeyDown_noSecurityMessageInteraction() { - ArgumentCaptor<KeyDownListener> onKeyDownListenerArgumentCaptor = - ArgumentCaptor.forClass(KeyDownListener.class); - verify(mAbsKeyInputView).setKeyDownListener(onKeyDownListenerArgumentCaptor.capture()); - onKeyDownListenerArgumentCaptor.getValue().onKeyDown( - KeyEvent.KEYCODE_UNKNOWN, mock(KeyEvent.class)); - verifyZeroInteractions(mKeyguardSecurityCallback); - verifyZeroInteractions(mKeyguardMessageAreaController); - } -} diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java index e7930795c7f8..5999e2cdec78 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java @@ -41,9 +41,11 @@ import android.widget.FrameLayout; import android.widget.TextClock; import com.android.systemui.R; +import com.android.systemui.SystemUIFactory; import com.android.systemui.SysuiTestCase; import com.android.systemui.plugins.ClockPlugin; import com.android.systemui.statusbar.StatusBarState; +import com.android.systemui.util.InjectionInflationController; import org.junit.Before; import org.junit.Test; @@ -76,7 +78,12 @@ public class KeyguardClockSwitchTest extends SysuiTestCase { when(mMockKeyguardSliceView.findViewById(R.id.keyguard_status_area)) .thenReturn(mMockKeyguardSliceView); - LayoutInflater layoutInflater = LayoutInflater.from(getContext()); + InjectionInflationController inflationController = new InjectionInflationController( + SystemUIFactory.getInstance() + .getSysUIComponent() + .createViewInstanceCreatorFactory()); + LayoutInflater layoutInflater = inflationController + .injectable(LayoutInflater.from(getContext())); layoutInflater.setPrivateFactory(new LayoutInflater.Factory2() { @Override diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaControllerTest.java deleted file mode 100644 index a7197cca530c..000000000000 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaControllerTest.java +++ /dev/null @@ -1,87 +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.keyguard; - -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.verify; - -import android.test.suitebuilder.annotation.SmallTest; -import android.testing.AndroidTestingRunner; - -import com.android.systemui.SysuiTestCase; -import com.android.systemui.statusbar.policy.ConfigurationController; -import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -@SmallTest -@RunWith(AndroidTestingRunner.class) -public class KeyguardMessageAreaControllerTest extends SysuiTestCase { - @Mock - private ConfigurationController mConfigurationController; - @Mock - private KeyguardUpdateMonitor mKeyguardUpdateMonitor; - @Mock - private KeyguardMessageArea mKeyguardMessageArea; - - private KeyguardMessageAreaController mMessageAreaController; - - @Before - public void setUp() throws Exception { - MockitoAnnotations.initMocks(this); - mMessageAreaController = new KeyguardMessageAreaController.Factory( - mKeyguardUpdateMonitor, mConfigurationController).create(mKeyguardMessageArea); - } - - @Test - public void onAttachedToWindow_registersConfigurationCallback() { - ArgumentCaptor<ConfigurationListener> configurationListenerArgumentCaptor = - ArgumentCaptor.forClass(ConfigurationListener.class); - - mMessageAreaController.onViewAttached(); - verify(mConfigurationController).addCallback(configurationListenerArgumentCaptor.capture()); - - mMessageAreaController.onViewDetached(); - verify(mConfigurationController).removeCallback( - eq(configurationListenerArgumentCaptor.getValue())); - } - - @Test - public void onAttachedToWindow_registersKeyguardUpdateMontiorCallback() { - ArgumentCaptor<KeyguardUpdateMonitorCallback> keyguardUpdateMonitorCallbackArgumentCaptor = - ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback.class); - - mMessageAreaController.onViewAttached(); - verify(mKeyguardUpdateMonitor).registerCallback( - keyguardUpdateMonitorCallbackArgumentCaptor.capture()); - - mMessageAreaController.onViewDetached(); - verify(mKeyguardUpdateMonitor).removeCallback( - eq(keyguardUpdateMonitorCallbackArgumentCaptor.getValue())); - } - - @Test - public void testClearsTextField() { - mMessageAreaController.setMessage(""); - verify(mKeyguardMessageArea).setMessage(""); - } -} diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaTest.java index 31fb25a7a89c..fc7b9a4b47d1 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 The Android Open Source Project + * Copyright (C) 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. @@ -11,60 +11,65 @@ * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and - * limitations under the License. + * limitations under the License */ package com.android.keyguard; -import static com.google.common.truth.Truth.assertThat; +import static junit.framework.Assert.assertEquals; + +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; import android.test.suitebuilder.annotation.SmallTest; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper.RunWithLooper; -import android.view.View; import com.android.systemui.SysuiTestCase; +import com.android.systemui.statusbar.policy.ConfigurationController; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.Mock; import org.mockito.MockitoAnnotations; @SmallTest @RunWith(AndroidTestingRunner.class) @RunWithLooper public class KeyguardMessageAreaTest extends SysuiTestCase { - private KeyguardMessageArea mKeyguardMessageArea; + @Mock + private ConfigurationController mConfigurationController; + @Mock + private KeyguardUpdateMonitor mKeyguardUpdateMonitor; + private KeyguardMessageArea mMessageArea; @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); - mKeyguardMessageArea = new KeyguardMessageArea(mContext, null); - mKeyguardMessageArea.setBouncerVisible(true); + mMessageArea = new KeyguardMessageArea(mContext, null, mKeyguardUpdateMonitor, + mConfigurationController); + waitForIdleSync(); } @Test - public void testShowsTextField() { - mKeyguardMessageArea.setVisibility(View.INVISIBLE); - mKeyguardMessageArea.setMessage("oobleck"); - assertThat(mKeyguardMessageArea.getVisibility()).isEqualTo(View.VISIBLE); - assertThat(mKeyguardMessageArea.getText()).isEqualTo("oobleck"); - } + public void onAttachedToWindow_registersConfigurationCallback() { + mMessageArea.onAttachedToWindow(); + verify(mConfigurationController).addCallback(eq(mMessageArea)); - @Test - public void testHiddenWhenBouncerHidden() { - mKeyguardMessageArea.setBouncerVisible(false); - mKeyguardMessageArea.setVisibility(View.INVISIBLE); - mKeyguardMessageArea.setMessage("oobleck"); - assertThat(mKeyguardMessageArea.getVisibility()).isEqualTo(View.INVISIBLE); - assertThat(mKeyguardMessageArea.getText()).isEqualTo("oobleck"); + mMessageArea.onDetachedFromWindow(); + verify(mConfigurationController).removeCallback(eq(mMessageArea)); } @Test - public void testClearsTextField() { - mKeyguardMessageArea.setVisibility(View.VISIBLE); - mKeyguardMessageArea.setMessage(""); - assertThat(mKeyguardMessageArea.getVisibility()).isEqualTo(View.INVISIBLE); - assertThat(mKeyguardMessageArea.getText()).isEqualTo(""); + public void clearFollowedByMessage_keepsMessage() { + mMessageArea.setMessage(""); + mMessageArea.setMessage("test"); + + CharSequence[] messageText = new CharSequence[1]; + messageText[0] = mMessageArea.getText(); + + assertEquals("test", messageText[0]); } + } diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt deleted file mode 100644 index c69ec1a254c3..000000000000 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt +++ /dev/null @@ -1,84 +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.keyguard - -import android.testing.AndroidTestingRunner -import android.testing.TestableLooper -import androidx.test.filters.SmallTest -import com.android.internal.util.LatencyTracker -import com.android.internal.widget.LockPatternUtils -import com.android.internal.widget.LockPatternView -import com.android.systemui.R -import com.android.systemui.SysuiTestCase -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith -import org.mockito.Mock -import org.mockito.Mockito.`when` -import org.mockito.Mockito.verify -import org.mockito.MockitoAnnotations - -@SmallTest -@RunWith(AndroidTestingRunner::class) -@TestableLooper.RunWithLooper -class KeyguardPatternViewControllerTest : SysuiTestCase() { - @Mock - private lateinit var mKeyguardPatternView: KeyguardPatternView - @Mock - private lateinit var mKeyguardUpdateMonitor: KeyguardUpdateMonitor - @Mock - private lateinit var mSecurityMode: KeyguardSecurityModel.SecurityMode - @Mock - private lateinit var mLockPatternUtils: LockPatternUtils - @Mock - private lateinit var mKeyguardSecurityCallback: KeyguardSecurityCallback - @Mock - private lateinit var mLatencyTracker: LatencyTracker - @Mock - private lateinit - var mKeyguardMessageAreaControllerFactory: KeyguardMessageAreaController.Factory - @Mock - private lateinit var mKeyguardMessageArea: KeyguardMessageArea - @Mock - private lateinit var mKeyguardMessageAreaController: KeyguardMessageAreaController - @Mock - private lateinit var mLockPatternView: LockPatternView - - private lateinit var mKeyguardPatternViewController: KeyguardPatternViewController - - @Before - fun setup() { - MockitoAnnotations.initMocks(this) - `when`(mKeyguardPatternView.isAttachedToWindow).thenReturn(true) - `when`(mKeyguardPatternView.findViewById<KeyguardMessageArea>(R.id.keyguard_message_area)) - .thenReturn(mKeyguardMessageArea) - `when`(mKeyguardPatternView.findViewById<LockPatternView>(R.id.lockPatternView)) - .thenReturn(mLockPatternView) - `when`(mKeyguardMessageAreaControllerFactory.create(mKeyguardMessageArea)) - .thenReturn(mKeyguardMessageAreaController) - mKeyguardPatternViewController = KeyguardPatternViewController(mKeyguardPatternView, - mKeyguardUpdateMonitor, mSecurityMode, mLockPatternUtils, mKeyguardSecurityCallback, - mLatencyTracker, mKeyguardMessageAreaControllerFactory) - } - - @Test - fun onPause_clearsTextField() { - mKeyguardPatternViewController.init() - mKeyguardPatternViewController.onPause() - verify(mKeyguardMessageAreaController).setMessage("") - } -} diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewTest.kt new file mode 100644 index 000000000000..b4363cf215f1 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewTest.kt @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.keyguard + +import androidx.test.filters.SmallTest +import android.testing.AndroidTestingRunner +import android.testing.TestableLooper +import android.view.LayoutInflater + +import com.android.systemui.R +import com.android.systemui.SysuiTestCase +import com.android.systemui.statusbar.policy.ConfigurationController +import com.google.common.truth.Truth.assertThat + +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mockito.mock + +@SmallTest +@RunWith(AndroidTestingRunner::class) +@TestableLooper.RunWithLooper +class KeyguardPatternViewTest : SysuiTestCase() { + + private lateinit var mKeyguardPatternView: KeyguardPatternView + private lateinit var mSecurityMessage: KeyguardMessageArea + + @Before + fun setup() { + val inflater = LayoutInflater.from(context) + mDependency.injectMockDependency(KeyguardUpdateMonitor::class.java) + mKeyguardPatternView = inflater.inflate(R.layout.keyguard_pattern_view, null) + as KeyguardPatternView + mSecurityMessage = KeyguardMessageArea(mContext, null, + mock(KeyguardUpdateMonitor::class.java), mock(ConfigurationController::class.java)) + mKeyguardPatternView.mSecurityMessageDisplay = mSecurityMessage + } + + @Test + fun onPause_clearsTextField() { + mSecurityMessage.setMessage("an old message") + mKeyguardPatternView.onPause() + assertThat(mSecurityMessage.text).isEqualTo("") + } +} diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java deleted file mode 100644 index 4944284698a0..000000000000 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java +++ /dev/null @@ -1,106 +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.keyguard; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.testing.AndroidTestingRunner; -import android.testing.TestableLooper.RunWithLooper; -import android.view.View; - -import androidx.test.filters.SmallTest; - -import com.android.internal.util.LatencyTracker; -import com.android.internal.widget.LockPatternUtils; -import com.android.keyguard.KeyguardSecurityModel.SecurityMode; -import com.android.systemui.R; -import com.android.systemui.SysuiTestCase; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -@SmallTest -@RunWith(AndroidTestingRunner.class) -@RunWithLooper -public class KeyguardPinBasedInputViewControllerTest extends SysuiTestCase { - - @Mock - private KeyguardPinBasedInputView mPinBasedInputView; - @Mock - private PasswordTextView mPasswordEntry; - @Mock - private KeyguardMessageArea mKeyguardMessageArea; - @Mock - private KeyguardUpdateMonitor mKeyguardUpdateMonitor; - @Mock - private SecurityMode mSecurityMode; - @Mock - private LockPatternUtils mLockPatternUtils; - @Mock - private KeyguardSecurityCallback mKeyguardSecurityCallback; - @Mock - private KeyguardMessageAreaController.Factory mKeyguardMessageAreaControllerFactory; - @Mock - private KeyguardMessageAreaController mKeyguardMessageAreaController; - @Mock - private LatencyTracker mLatencyTracker; - @Mock - private LiftToActivateListener mLiftToactivateListener; - @Mock - private View mDeleteButton; - @Mock - private View mOkButton; - - private KeyguardPinBasedInputViewController mKeyguardPinViewController; - - @Before - public void setup() { - MockitoAnnotations.initMocks(this); - when(mKeyguardMessageAreaControllerFactory.create(any(KeyguardMessageArea.class))) - .thenReturn(mKeyguardMessageAreaController); - when(mPinBasedInputView.getPasswordTextViewId()).thenReturn(1); - when(mPinBasedInputView.findViewById(1)).thenReturn(mPasswordEntry); - when(mPinBasedInputView.isAttachedToWindow()).thenReturn(true); - when(mPinBasedInputView.findViewById(R.id.keyguard_message_area)) - .thenReturn(mKeyguardMessageArea); - when(mPinBasedInputView.findViewById(R.id.delete_button)) - .thenReturn(mDeleteButton); - when(mPinBasedInputView.findViewById(R.id.key_enter)) - .thenReturn(mOkButton); - mKeyguardPinViewController = new KeyguardPinBasedInputViewController(mPinBasedInputView, - mKeyguardUpdateMonitor, mSecurityMode, mLockPatternUtils, mKeyguardSecurityCallback, - mKeyguardMessageAreaControllerFactory, mLatencyTracker, mLiftToactivateListener) { - @Override - public void onResume(int reason) { - super.onResume(reason); - } - }; - mKeyguardPinViewController.init(); - } - - @Test - public void onResume_requestsFocus() { - mKeyguardPinViewController.onResume(KeyguardSecurityView.SCREEN_ON); - verify(mPasswordEntry).requestFocus(); - } -} - diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewTest.java new file mode 100644 index 000000000000..6666a926c68b --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewTest.java @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.keyguard; + +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyZeroInteractions; + +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper.RunWithLooper; +import android.view.KeyEvent; +import android.view.LayoutInflater; + +import androidx.test.filters.SmallTest; + +import com.android.systemui.R; +import com.android.systemui.SysuiTestCase; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +@SmallTest +@RunWith(AndroidTestingRunner.class) +@RunWithLooper +public class KeyguardPinBasedInputViewTest extends SysuiTestCase { + + @Mock + private PasswordTextView mPasswordEntry; + @Mock + private SecurityMessageDisplay mSecurityMessageDisplay; + @InjectMocks + private KeyguardPinBasedInputView mKeyguardPinView; + + @Before + public void setup() { + LayoutInflater inflater = LayoutInflater.from(getContext()); + mDependency.injectMockDependency(KeyguardUpdateMonitor.class); + mKeyguardPinView = + (KeyguardPinBasedInputView) inflater.inflate(R.layout.keyguard_pin_view, null); + MockitoAnnotations.initMocks(this); + } + + @Test + public void onResume_requestsFocus() { + mKeyguardPinView.onResume(KeyguardSecurityView.SCREEN_ON); + verify(mPasswordEntry).requestFocus(); + } + + @Test + public void onKeyDown_clearsSecurityMessage() { + mKeyguardPinView.onKeyDown(KeyEvent.KEYCODE_0, mock(KeyEvent.class)); + verify(mSecurityMessageDisplay).setMessage(eq("")); + } + + @Test + public void onKeyDown_noSecurityMessageInteraction() { + mKeyguardPinView.onKeyDown(KeyEvent.KEYCODE_UNKNOWN, mock(KeyEvent.class)); + verifyZeroInteractions(mSecurityMessageDisplay); + } +} diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPresentationTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPresentationTest.java index ae159c73b99f..559284ac0672 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPresentationTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPresentationTest.java @@ -31,7 +31,9 @@ import androidx.test.filters.SmallTest; import com.android.keyguard.KeyguardDisplayManager.KeyguardPresentation; import com.android.keyguard.dagger.KeyguardStatusViewComponent; import com.android.systemui.R; +import com.android.systemui.SystemUIFactory; import com.android.systemui.SysuiTestCase; +import com.android.systemui.util.InjectionInflationController; import org.junit.After; import org.junit.Before; @@ -63,6 +65,7 @@ public class KeyguardPresentationTest extends SysuiTestCase { @Before public void setUp() { MockitoAnnotations.initMocks(this); + mDependency.injectMockDependency(KeyguardUpdateMonitor.class); when(mMockKeyguardClockSwitch.getContext()).thenReturn(mContext); when(mMockKeyguardSliceView.getContext()).thenReturn(mContext); when(mMockKeyguardStatusView.getContext()).thenReturn(mContext); @@ -74,7 +77,11 @@ public class KeyguardPresentationTest extends SysuiTestCase { allowTestableLooperAsMainThread(); - mLayoutInflater = LayoutInflater.from(mContext); + InjectionInflationController inflationController = new InjectionInflationController( + SystemUIFactory.getInstance() + .getSysUIComponent() + .createViewInstanceCreatorFactory()); + mLayoutInflater = inflationController.injectable(LayoutInflater.from(mContext)); mLayoutInflater.setPrivateFactory(new LayoutInflater.Factory2() { @Override diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java deleted file mode 100644 index cdb91ecfad89..000000000000 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java +++ /dev/null @@ -1,130 +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.keyguard; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.testing.AndroidTestingRunner; -import android.testing.TestableLooper; -import android.view.WindowInsetsController; - -import androidx.test.filters.SmallTest; - -import com.android.internal.logging.MetricsLogger; -import com.android.internal.logging.UiEventLogger; -import com.android.internal.widget.LockPatternUtils; -import com.android.keyguard.KeyguardSecurityModel.SecurityMode; -import com.android.systemui.SysuiTestCase; -import com.android.systemui.statusbar.policy.KeyguardStateController; - -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.junit.MockitoJUnit; -import org.mockito.junit.MockitoRule; - -@SmallTest -@RunWith(AndroidTestingRunner.class) -@TestableLooper.RunWithLooper() -public class KeyguardSecurityContainerControllerTest extends SysuiTestCase { - - @Rule - public MockitoRule mRule = MockitoJUnit.rule(); - - @Mock - private KeyguardSecurityContainer mView; - @Mock - private AdminSecondaryLockScreenController.Factory mAdminSecondaryLockScreenControllerFactory; - @Mock - private AdminSecondaryLockScreenController mAdminSecondaryLockScreenController; - @Mock - private LockPatternUtils mLockPatternUtils; - @Mock - private KeyguardUpdateMonitor mKeyguardUpdateMonitor; - @Mock - private KeyguardSecurityModel mKeyguardSecurityModel; - @Mock - private MetricsLogger mMetricsLogger; - @Mock - private UiEventLogger mUiEventLogger; - @Mock - private KeyguardStateController mKeyguardStateController; - @Mock - private KeyguardInputViewController mInputViewController; - @Mock - private KeyguardSecurityContainer.SecurityCallback mSecurityCallback; - @Mock - private WindowInsetsController mWindowInsetsController; - @Mock - private KeyguardSecurityViewFlipper mSecurityViewFlipper; - @Mock - private KeyguardSecurityViewFlipperController mKeyguardSecurityViewFlipperController; - - private KeyguardSecurityContainerController mKeyguardSecurityContainerController; - - @Before - public void setup() { - when(mAdminSecondaryLockScreenControllerFactory.create(any(KeyguardSecurityCallback.class))) - .thenReturn(mAdminSecondaryLockScreenController); - when(mSecurityViewFlipper.getWindowInsetsController()).thenReturn(mWindowInsetsController); - - mKeyguardSecurityContainerController = new KeyguardSecurityContainerController( - mView, mAdminSecondaryLockScreenControllerFactory, mLockPatternUtils, - mKeyguardUpdateMonitor, mKeyguardSecurityModel, mMetricsLogger, mUiEventLogger, - mKeyguardStateController, mKeyguardSecurityViewFlipperController); - - mKeyguardSecurityContainerController.setSecurityCallback(mSecurityCallback); - } - - @Test - public void showSecurityScreen_canInflateAllModes() { - SecurityMode[] modes = SecurityMode.values(); - for (SecurityMode mode : modes) { - when(mInputViewController.getSecurityMode()).thenReturn(mode); - mKeyguardSecurityContainerController.showSecurityScreen(mode); - if (mode == SecurityMode.Invalid) { - verify(mKeyguardSecurityViewFlipperController, never()).getSecurityView( - any(SecurityMode.class), any(KeyguardSecurityCallback.class)); - } else { - verify(mKeyguardSecurityViewFlipperController).getSecurityView( - eq(mode), any(KeyguardSecurityCallback.class)); - } - } - } - - @Test - public void startDisappearAnimation_animatesKeyboard() { - when(mKeyguardSecurityModel.getSecurityMode(anyInt())).thenReturn( - SecurityMode.Password); - when(mInputViewController.getSecurityMode()).thenReturn( - SecurityMode.Password); - when(mKeyguardSecurityViewFlipperController.getSecurityView( - eq(SecurityMode.Password), any(KeyguardSecurityCallback.class))) - .thenReturn(mInputViewController); - mKeyguardSecurityContainerController.showPrimarySecurityScreen(false /* turningOff */); - - mKeyguardSecurityContainerController.startDisappearAnimation(null); - verify(mInputViewController).startDisappearAnimation(eq(null)); - } -} diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java index 854be1f76722..a867825e223d 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java @@ -19,19 +19,23 @@ package com.android.keyguard; import static android.view.WindowInsets.Type.ime; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.content.Context; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; +import android.view.LayoutInflater; import android.view.WindowInsetsController; import androidx.test.filters.SmallTest; -import com.android.keyguard.KeyguardSecurityModel.SecurityMode; +import com.android.systemui.R; import com.android.systemui.SysuiTestCase; +import com.android.systemui.statusbar.policy.KeyguardStateController; import org.junit.Before; import org.junit.Rule; @@ -46,26 +50,68 @@ import org.mockito.junit.MockitoRule; @TestableLooper.RunWithLooper() public class KeyguardSecurityContainerTest extends SysuiTestCase { - @Rule - public MockitoRule mRule = MockitoJUnit.rule(); - + @Mock + private KeyguardSecurityModel mKeyguardSecurityModel; + @Mock + private KeyguardStateController mKeyguardStateController; + @Mock + private KeyguardUpdateMonitor mKeyguardUpdateMonitor; + @Mock + private KeyguardSecurityContainer.SecurityCallback mSecurityCallback; + @Mock + private KeyguardSecurityView mSecurityView; @Mock private WindowInsetsController mWindowInsetsController; @Mock private KeyguardSecurityViewFlipper mSecurityViewFlipper; - + @Rule + public MockitoRule mRule = MockitoJUnit.rule(); private KeyguardSecurityContainer mKeyguardSecurityContainer; @Before public void setup() { - when(mSecurityViewFlipper.getWindowInsetsController()).thenReturn(mWindowInsetsController); - mKeyguardSecurityContainer = new KeyguardSecurityContainer(getContext()); + mDependency.injectTestDependency(KeyguardStateController.class, mKeyguardStateController); + mDependency.injectTestDependency(KeyguardSecurityModel.class, mKeyguardSecurityModel); + mDependency.injectTestDependency(KeyguardUpdateMonitor.class, mKeyguardUpdateMonitor); + mKeyguardSecurityContainer = new KeyguardSecurityContainer(getContext()) { + @Override + protected KeyguardSecurityView getSecurityView( + KeyguardSecurityModel.SecurityMode securityMode) { + return mSecurityView; + } + }; mKeyguardSecurityContainer.mSecurityViewFlipper = mSecurityViewFlipper; + when(mSecurityViewFlipper.getWindowInsetsController()).thenReturn(mWindowInsetsController); + mKeyguardSecurityContainer.setSecurityCallback(mSecurityCallback); + } + + @Test + public void showSecurityScreen_canInflateAllModes() { + Context context = getContext(); + + for (int theme : new int[] {R.style.Theme_SystemUI, R.style.Theme_SystemUI_Light}) { + context.setTheme(theme); + final LayoutInflater inflater = LayoutInflater.from(context); + KeyguardSecurityModel.SecurityMode[] modes = + KeyguardSecurityModel.SecurityMode.values(); + for (KeyguardSecurityModel.SecurityMode mode : modes) { + final int resId = mKeyguardSecurityContainer.getLayoutIdFor(mode); + if (resId == 0) { + continue; + } + inflater.inflate(resId, null /* root */, false /* attach */); + } + } } @Test public void startDisappearAnimation_animatesKeyboard() { - mKeyguardSecurityContainer.startDisappearAnimation(SecurityMode.Password); + when(mKeyguardSecurityModel.getSecurityMode(anyInt())).thenReturn( + KeyguardSecurityModel.SecurityMode.Password); + mKeyguardSecurityContainer.showPrimarySecurityScreen(false /* turningOff */); + + mKeyguardSecurityContainer.startDisappearAnimation(null); + verify(mSecurityView).startDisappearAnimation(eq(null)); verify(mWindowInsetsController).controlWindowInsetsAnimation(eq(ime()), anyLong(), any(), any(), any()); } diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityViewFlipperControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityViewFlipperControllerTest.java deleted file mode 100644 index 3b7f4b839853..000000000000 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityViewFlipperControllerTest.java +++ /dev/null @@ -1,102 +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.keyguard; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyBoolean; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.testing.AndroidTestingRunner; -import android.testing.TestableLooper; -import android.view.LayoutInflater; -import android.view.ViewGroup; -import android.view.WindowInsetsController; - -import androidx.test.filters.SmallTest; - -import com.android.keyguard.KeyguardSecurityModel.SecurityMode; -import com.android.systemui.SysuiTestCase; - -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.junit.MockitoJUnit; -import org.mockito.junit.MockitoRule; - -@SmallTest -@RunWith(AndroidTestingRunner.class) -@TestableLooper.RunWithLooper() -public class KeyguardSecurityViewFlipperControllerTest extends SysuiTestCase { - - @Rule - public MockitoRule mRule = MockitoJUnit.rule(); - - @Mock - private KeyguardSecurityViewFlipper mView; - @Mock - private LayoutInflater mLayoutInflater; - @Mock - private KeyguardInputViewController.Factory mKeyguardSecurityViewControllerFactory; - @Mock - private KeyguardInputViewController mKeyguardInputViewController; - @Mock - private KeyguardInputView mInputView; - @Mock - private WindowInsetsController mWindowInsetsController; - @Mock - private KeyguardSecurityCallback mKeyguardSecurityCallback; - - private KeyguardSecurityViewFlipperController mKeyguardSecurityViewFlipperController; - - @Before - public void setup() { - when(mKeyguardSecurityViewControllerFactory.create( - any(KeyguardInputView.class), any(SecurityMode.class), - any(KeyguardSecurityCallback.class))) - .thenReturn(mKeyguardInputViewController); - when(mView.getWindowInsetsController()).thenReturn(mWindowInsetsController); - - mKeyguardSecurityViewFlipperController = new KeyguardSecurityViewFlipperController(mView, - mLayoutInflater, mKeyguardSecurityViewControllerFactory); - } - - @Test - public void showSecurityScreen_canInflateAllModes() { - SecurityMode[] modes = SecurityMode.values(); - // Always return an invalid controller so that we're always making a new one. - when(mKeyguardInputViewController.getSecurityMode()).thenReturn(SecurityMode.Invalid); - for (SecurityMode mode : modes) { - reset(mLayoutInflater); - when(mLayoutInflater.inflate(anyInt(), eq(mView), eq(false))) - .thenReturn(mInputView); - mKeyguardSecurityViewFlipperController.getSecurityView(mode, mKeyguardSecurityCallback); - if (mode == SecurityMode.Invalid || mode == SecurityMode.None) { - verify(mLayoutInflater, never()).inflate( - anyInt(), any(ViewGroup.class), anyBoolean()); - } else { - verify(mLayoutInflater).inflate(anyInt(), eq(mView), eq(false)); - } - } - } -} diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewTest.java index 79ec4f2c553a..0431704778c3 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewTest.java @@ -24,7 +24,9 @@ import android.testing.TestableLooper.RunWithLooper; import android.view.LayoutInflater; import com.android.systemui.R; +import com.android.systemui.SystemUIFactory; import com.android.systemui.SysuiTestCase; +import com.android.systemui.util.InjectionInflationController; import org.junit.Before; import org.junit.Test; @@ -48,7 +50,13 @@ public class KeyguardStatusViewTest extends SysuiTestCase { @Before public void setUp() { allowTestableLooperAsMainThread(); - LayoutInflater layoutInflater = LayoutInflater.from(getContext()); + mDependency.injectMockDependency(KeyguardUpdateMonitor.class); + InjectionInflationController inflationController = new InjectionInflationController( + SystemUIFactory.getInstance() + .getSysUIComponent() + .createViewInstanceCreatorFactory()); + LayoutInflater layoutInflater = inflationController + .injectable(LayoutInflater.from(getContext())); mKeyguardStatusView = (KeyguardStatusView) layoutInflater.inflate(R.layout.keyguard_status_view, null); org.mockito.MockitoAnnotations.initMocks(this); diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java index 42b21c61510a..27b5b7fda684 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java @@ -36,7 +36,6 @@ import androidx.core.graphics.drawable.IconCompat; import androidx.test.filters.SmallTest; import com.android.settingslib.bluetooth.LocalBluetoothManager; -import com.android.settingslib.media.MediaDevice; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; import com.android.systemui.plugins.ActivityStarter; @@ -55,7 +54,6 @@ public class MediaOutputBaseDialogTest extends SysuiTestCase { // Mock private MediaOutputBaseAdapter mMediaOutputBaseAdapter = mock(MediaOutputBaseAdapter.class); - private MediaSessionManager mMediaSessionManager = mock(MediaSessionManager.class); private LocalBluetoothManager mLocalBluetoothManager = mock(LocalBluetoothManager.class); private ShadeController mShadeController = mock(ShadeController.class); @@ -157,30 +155,6 @@ public class MediaOutputBaseDialogTest extends SysuiTestCase { verify(mMediaOutputBaseAdapter).notifyDataSetChanged(); } - @Test - public void refresh_with6Devices_checkBottomPaddingVisibility() { - for (int i = 0; i < 6; i++) { - mMediaOutputController.mMediaDevices.add(mock(MediaDevice.class)); - } - mMediaOutputBaseDialogImpl.refresh(); - final View view = mMediaOutputBaseDialogImpl.mDialogView.requireViewById( - R.id.list_bottom_padding); - - assertThat(view.getVisibility()).isEqualTo(View.GONE); - } - - @Test - public void refresh_with5Devices_checkBottomPaddingVisibility() { - for (int i = 0; i < 5; i++) { - mMediaOutputController.mMediaDevices.add(mock(MediaDevice.class)); - } - mMediaOutputBaseDialogImpl.refresh(); - final View view = mMediaOutputBaseDialogImpl.mDialogView.requireViewById( - R.id.list_bottom_padding); - - assertThat(view.getVisibility()).isEqualTo(View.VISIBLE); - } - class MediaOutputBaseDialogImpl extends MediaOutputBaseDialog { MediaOutputBaseDialogImpl(Context context, MediaOutputController mediaOutputController) { @@ -189,24 +163,34 @@ public class MediaOutputBaseDialogTest extends SysuiTestCase { mAdapter = mMediaOutputBaseAdapter; } + @Override int getHeaderIconRes() { return mHeaderIconRes; } + @Override IconCompat getHeaderIcon() { return mIconCompat; } + @Override int getHeaderIconSize() { return 10; } + @Override CharSequence getHeaderText() { return mHeaderTitle; } + @Override CharSequence getHeaderSubtitle() { return mHeaderSubtitle; } + + @Override + int getStopButtonVisibility() { + return 0; + } } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java new file mode 100644 index 000000000000..ca328fbe44fb --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java @@ -0,0 +1,105 @@ +/* + * 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.systemui.media.dialog; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import android.media.MediaRoute2Info; +import android.media.session.MediaSessionManager; +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; +import android.view.View; + +import androidx.test.filters.SmallTest; + +import com.android.settingslib.bluetooth.LocalBluetoothManager; +import com.android.settingslib.media.LocalMediaManager; +import com.android.settingslib.media.MediaDevice; +import com.android.systemui.SysuiTestCase; +import com.android.systemui.plugins.ActivityStarter; +import com.android.systemui.statusbar.phone.ShadeController; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.ArrayList; +import java.util.List; + +@SmallTest +@RunWith(AndroidTestingRunner.class) +@TestableLooper.RunWithLooper +public class MediaOutputDialogTest extends SysuiTestCase { + + private static final String TEST_PACKAGE = "test_package"; + + // Mock + private MediaSessionManager mMediaSessionManager = mock(MediaSessionManager.class); + private LocalBluetoothManager mLocalBluetoothManager = mock(LocalBluetoothManager.class); + private ShadeController mShadeController = mock(ShadeController.class); + private ActivityStarter mStarter = mock(ActivityStarter.class); + private LocalMediaManager mLocalMediaManager = mock(LocalMediaManager.class); + private MediaDevice mMediaDevice = mock(MediaDevice.class); + + private MediaOutputDialog mMediaOutputDialog; + private MediaOutputController mMediaOutputController; + private List<String> mFeatures = new ArrayList<>(); + + @Before + public void setUp() { + mMediaOutputController = new MediaOutputController(mContext, TEST_PACKAGE, + mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter); + mMediaOutputController.mLocalMediaManager = mLocalMediaManager; + mMediaOutputDialog = new MediaOutputDialog(mContext, false, mMediaOutputController); + + when(mLocalMediaManager.getCurrentConnectedDevice()).thenReturn(mMediaDevice); + when(mMediaDevice.getFeatures()).thenReturn(mFeatures); + } + + @Test + public void getStopButtonVisibility_remoteDevice_returnVisible() { + mFeatures.add(MediaRoute2Info.FEATURE_REMOTE_PLAYBACK); + + assertThat(mMediaOutputDialog.getStopButtonVisibility()).isEqualTo(View.VISIBLE); + + mFeatures.clear(); + mFeatures.add(MediaRoute2Info.FEATURE_REMOTE_AUDIO_PLAYBACK); + + assertThat(mMediaOutputDialog.getStopButtonVisibility()).isEqualTo(View.VISIBLE); + + mFeatures.clear(); + mFeatures.add(MediaRoute2Info.FEATURE_REMOTE_VIDEO_PLAYBACK); + + assertThat(mMediaOutputDialog.getStopButtonVisibility()).isEqualTo(View.VISIBLE); + + mFeatures.clear(); + mFeatures.add(MediaRoute2Info.FEATURE_REMOTE_GROUP_PLAYBACK); + + assertThat(mMediaOutputDialog.getStopButtonVisibility()).isEqualTo(View.VISIBLE); + } + + @Test + public void getStopButtonVisibility_localDevice_returnGone() { + mFeatures.add(MediaRoute2Info.FEATURE_LOCAL_PLAYBACK); + + assertThat(mMediaOutputDialog.getStopButtonVisibility()).isEqualTo(View.GONE); + } + +} diff --git a/packages/Tethering/jni/android_net_util_TetheringUtils.cpp b/packages/Tethering/jni/android_net_util_TetheringUtils.cpp index f6eb40a842d5..94c871d8a366 100644 --- a/packages/Tethering/jni/android_net_util_TetheringUtils.cpp +++ b/packages/Tethering/jni/android_net_util_TetheringUtils.cpp @@ -17,18 +17,63 @@ #include <errno.h> #include <error.h> #include <jni.h> +#include <linux/filter.h> #include <nativehelper/JNIHelp.h> #include <nativehelper/JNIHelpCompat.h> #include <nativehelper/ScopedUtfChars.h> #include <net/if.h> +#include <netinet/ether.h> +#include <netinet/ip6.h> #include <netinet/icmp6.h> #include <sys/socket.h> +#include <stdio.h> #define LOG_TAG "TetheringUtils" #include <android/log.h> namespace android { +static const uint32_t kIPv6NextHeaderOffset = offsetof(ip6_hdr, ip6_nxt); +static const uint32_t kIPv6PayloadStart = sizeof(ip6_hdr); +static const uint32_t kICMPv6TypeOffset = kIPv6PayloadStart + offsetof(icmp6_hdr, icmp6_type); + +static void android_net_util_setupIcmpFilter(JNIEnv *env, jobject javaFd, uint32_t type) { + sock_filter filter_code[] = { + // Check header is ICMPv6. + BPF_STMT(BPF_LD | BPF_B | BPF_ABS, kIPv6NextHeaderOffset), + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, IPPROTO_ICMPV6, 0, 3), + + // Check ICMPv6 type. + BPF_STMT(BPF_LD | BPF_B | BPF_ABS, kICMPv6TypeOffset), + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, type, 0, 1), + + // Accept or reject. + BPF_STMT(BPF_RET | BPF_K, 0xffff), + BPF_STMT(BPF_RET | BPF_K, 0) + }; + + const sock_fprog filter = { + sizeof(filter_code) / sizeof(filter_code[0]), + filter_code, + }; + + int fd = jniGetFDFromFileDescriptor(env, javaFd); + if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter)) != 0) { + jniThrowExceptionFmt(env, "java/net/SocketException", + "setsockopt(SO_ATTACH_FILTER): %s", strerror(errno)); + } +} + +static void android_net_util_setupNaSocket(JNIEnv *env, jobject clazz, jobject javaFd) +{ + android_net_util_setupIcmpFilter(env, javaFd, ND_NEIGHBOR_ADVERT); +} + +static void android_net_util_setupNsSocket(JNIEnv *env, jobject clazz, jobject javaFd) +{ + android_net_util_setupIcmpFilter(env, javaFd, ND_NEIGHBOR_SOLICIT); +} + static void android_net_util_setupRaSocket(JNIEnv *env, jobject clazz, jobject javaFd, jint ifIndex) { @@ -125,7 +170,12 @@ static void android_net_util_setupRaSocket(JNIEnv *env, jobject clazz, jobject j */ static const JNINativeMethod gMethods[] = { /* name, signature, funcPtr */ - { "setupRaSocket", "(Ljava/io/FileDescriptor;I)V", (void*) android_net_util_setupRaSocket }, + { "setupNaSocket", "(Ljava/io/FileDescriptor;)V", + (void*) android_net_util_setupNaSocket }, + { "setupNsSocket", "(Ljava/io/FileDescriptor;)V", + (void*) android_net_util_setupNsSocket }, + { "setupRaSocket", "(Ljava/io/FileDescriptor;I)V", + (void*) android_net_util_setupRaSocket }, }; int register_android_net_util_TetheringUtils(JNIEnv* env) { diff --git a/packages/Tethering/src/android/net/ip/DadProxy.java b/packages/Tethering/src/android/net/ip/DadProxy.java new file mode 100644 index 000000000000..e2976b78908c --- /dev/null +++ b/packages/Tethering/src/android/net/ip/DadProxy.java @@ -0,0 +1,54 @@ +/* + * 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.net.ip; + +import android.net.util.InterfaceParams; +import android.os.Handler; + +import androidx.annotation.VisibleForTesting; + +/** + * Basic Duplicate address detection proxy. + * + * @hide + */ +public class DadProxy { + private static final String TAG = DadProxy.class.getSimpleName(); + + @VisibleForTesting + public static NeighborPacketForwarder naForwarder; + public static NeighborPacketForwarder nsForwarder; + + public DadProxy(Handler h, InterfaceParams tetheredIface) { + naForwarder = new NeighborPacketForwarder(h, tetheredIface, + NeighborPacketForwarder.ICMPV6_NEIGHBOR_ADVERTISEMENT); + nsForwarder = new NeighborPacketForwarder(h, tetheredIface, + NeighborPacketForwarder.ICMPV6_NEIGHBOR_SOLICITATION); + } + + /** Stop NS/NA Forwarders. */ + public void stop() { + naForwarder.stop(); + nsForwarder.stop(); + } + + /** Set upstream iface on both forwarders. */ + public void setUpstreamIface(InterfaceParams upstreamIface) { + naForwarder.setUpstreamIface(upstreamIface); + nsForwarder.setUpstreamIface(upstreamIface); + } +} diff --git a/packages/Tethering/src/android/net/ip/IpServer.java b/packages/Tethering/src/android/net/ip/IpServer.java index 673cbf09d259..336124dc1b90 100644 --- a/packages/Tethering/src/android/net/ip/IpServer.java +++ b/packages/Tethering/src/android/net/ip/IpServer.java @@ -51,6 +51,7 @@ import android.net.util.InterfaceParams; import android.net.util.InterfaceSet; import android.net.util.PrefixUtils; import android.net.util.SharedLog; +import android.os.Build; import android.os.Handler; import android.os.Looper; import android.os.Message; @@ -160,6 +161,15 @@ public class IpServer extends StateMachine { /** Capture IpServer dependencies, for injection. */ public abstract static class Dependencies { + /** + * Create a DadProxy instance to be used by IpServer. + * To support multiple tethered interfaces concurrently DAD Proxy + * needs to be supported per IpServer instead of per upstream. + */ + public DadProxy getDadProxy(Handler handler, InterfaceParams ifParams) { + return new DadProxy(handler, ifParams); + } + /** Create an IpNeighborMonitor to be used by this IpServer */ public IpNeighborMonitor getIpNeighborMonitor(Handler handler, SharedLog log, IpNeighborMonitor.NeighborEventConsumer consumer) { @@ -256,6 +266,7 @@ public class IpServer extends StateMachine { // Advertisements (otherwise, we do not add them to mLinkProperties at all). private LinkProperties mLastIPv6LinkProperties; private RouterAdvertisementDaemon mRaDaemon; + private DadProxy mDadProxy; // To be accessed only on the handler thread private int mDhcpServerStartIndex = 0; @@ -674,6 +685,13 @@ public class IpServer extends StateMachine { return false; } + // TODO: use ShimUtils instead of explicitly checking the version here. + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.R || "S".equals(Build.VERSION.CODENAME) + || "T".equals(Build.VERSION.CODENAME)) { + // DAD Proxy starts forwarding packets after IPv6 upstream is present. + mDadProxy = mDeps.getDadProxy(getHandler(), mInterfaceParams); + } + return true; } @@ -685,6 +703,11 @@ public class IpServer extends StateMachine { mRaDaemon.stop(); mRaDaemon = null; } + + if (mDadProxy != null) { + mDadProxy.stop(); + mDadProxy = null; + } } // IPv6TetheringCoordinator sends updates with carefully curated IPv6-only @@ -702,11 +725,16 @@ public class IpServer extends StateMachine { } RaParams params = null; - int upstreamIfindex = 0; + String upstreamIface = null; + InterfaceParams upstreamIfaceParams = null; + int upstreamIfIndex = 0; if (v6only != null) { - final String upstreamIface = v6only.getInterfaceName(); - + upstreamIface = v6only.getInterfaceName(); + upstreamIfaceParams = mDeps.getInterfaceParams(upstreamIface); + if (upstreamIfaceParams != null) { + upstreamIfIndex = upstreamIfaceParams.index; + } params = new RaParams(); params.mtu = v6only.getMtu(); params.hasDefaultRoute = v6only.hasIpv6DefaultRoute(); @@ -726,15 +754,13 @@ public class IpServer extends StateMachine { } } - upstreamIfindex = mDeps.getIfindex(upstreamIface); - // Add upstream index to name mapping for the tether stats usage in the coordinator. // Although this mapping could be added by both class Tethering and IpServer, adding // mapping from IpServer guarantees that the mapping is added before the adding // forwarding rules. That is because there are different state machines in both // classes. It is hard to guarantee the link property update order between multiple // state machines. - mBpfCoordinator.addUpstreamNameToLookupTable(upstreamIfindex, upstreamIface); + mBpfCoordinator.addUpstreamNameToLookupTable(upstreamIfIndex, upstreamIface); } // If v6only is null, we pass in null to setRaParams(), which handles @@ -743,8 +769,11 @@ public class IpServer extends StateMachine { setRaParams(params); mLastIPv6LinkProperties = v6only; - updateIpv6ForwardingRules(mLastIPv6UpstreamIfindex, upstreamIfindex, null); - mLastIPv6UpstreamIfindex = upstreamIfindex; + updateIpv6ForwardingRules(mLastIPv6UpstreamIfindex, upstreamIfIndex, null); + mLastIPv6UpstreamIfindex = upstreamIfIndex; + if (mDadProxy != null) { + mDadProxy.setUpstreamIface(upstreamIfaceParams); + } } private void removeRoutesFromLocalNetwork(@NonNull final List<RouteInfo> toBeRemoved) { diff --git a/packages/Tethering/src/android/net/ip/NeighborPacketForwarder.java b/packages/Tethering/src/android/net/ip/NeighborPacketForwarder.java new file mode 100644 index 000000000000..73fc833fabf5 --- /dev/null +++ b/packages/Tethering/src/android/net/ip/NeighborPacketForwarder.java @@ -0,0 +1,180 @@ +/* + * 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.net.ip; + +import static android.system.OsConstants.AF_INET6; +import static android.system.OsConstants.AF_PACKET; +import static android.system.OsConstants.ETH_P_IPV6; +import static android.system.OsConstants.IPPROTO_RAW; +import static android.system.OsConstants.SOCK_DGRAM; +import static android.system.OsConstants.SOCK_NONBLOCK; +import static android.system.OsConstants.SOCK_RAW; + +import android.net.util.InterfaceParams; +import android.net.util.PacketReader; +import android.net.util.SocketUtils; +import android.net.util.TetheringUtils; +import android.os.Handler; +import android.system.ErrnoException; +import android.system.Os; +import android.util.Log; + +import java.io.FileDescriptor; +import java.io.IOException; +import java.net.Inet6Address; +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.net.SocketException; +import java.net.UnknownHostException; +import java.util.Arrays; + +/** + * Basic IPv6 Neighbor Advertisement Forwarder. + * + * Forward NA packets from upstream iface to tethered iface + * and NS packets from tethered iface to upstream iface. + * + * @hide + */ +public class NeighborPacketForwarder extends PacketReader { + private final String mTag; + + private FileDescriptor mFd; + + // TODO: get these from NetworkStackConstants. + private static final int IPV6_ADDR_LEN = 16; + private static final int IPV6_DST_ADDR_OFFSET = 24; + private static final int IPV6_HEADER_LEN = 40; + private static final int ETH_HEADER_LEN = 14; + + private InterfaceParams mListenIfaceParams, mSendIfaceParams; + + private final int mType; + public static final int ICMPV6_NEIGHBOR_ADVERTISEMENT = 136; + public static final int ICMPV6_NEIGHBOR_SOLICITATION = 135; + + public NeighborPacketForwarder(Handler h, InterfaceParams tetheredInterface, int type) { + super(h); + mTag = NeighborPacketForwarder.class.getSimpleName() + "-" + + tetheredInterface.name + "-" + type; + mType = type; + + if (mType == ICMPV6_NEIGHBOR_ADVERTISEMENT) { + mSendIfaceParams = tetheredInterface; + } else { + mListenIfaceParams = tetheredInterface; + } + } + + /** Set new upstream iface and start/stop based on new params. */ + public void setUpstreamIface(InterfaceParams upstreamParams) { + final InterfaceParams oldUpstreamParams; + + if (mType == ICMPV6_NEIGHBOR_ADVERTISEMENT) { + oldUpstreamParams = mListenIfaceParams; + mListenIfaceParams = upstreamParams; + } else { + oldUpstreamParams = mSendIfaceParams; + mSendIfaceParams = upstreamParams; + } + + if (oldUpstreamParams == null && upstreamParams != null) { + start(); + } else if (oldUpstreamParams != null && upstreamParams == null) { + stop(); + } else if (oldUpstreamParams != null && upstreamParams != null + && oldUpstreamParams.index != upstreamParams.index) { + stop(); + start(); + } + } + + // TODO: move NetworkStackUtils.closeSocketQuietly to + // frameworks/libs/net/common/device/com/android/net/module/util/[someclass]. + private void closeSocketQuietly(FileDescriptor fd) { + try { + SocketUtils.closeSocket(fd); + } catch (IOException ignored) { + } + } + + @Override + protected FileDescriptor createFd() { + try { + // ICMPv6 packets from modem do not have eth header, so RAW socket cannot be used. + // To keep uniformity in both directions PACKET socket can be used. + mFd = Os.socket(AF_PACKET, SOCK_DGRAM | SOCK_NONBLOCK, 0); + + // TODO: convert setup*Socket to setupICMPv6BpfFilter with filter type? + if (mType == ICMPV6_NEIGHBOR_ADVERTISEMENT) { + TetheringUtils.setupNaSocket(mFd); + } else if (mType == ICMPV6_NEIGHBOR_SOLICITATION) { + TetheringUtils.setupNsSocket(mFd); + } + + SocketAddress bindAddress = SocketUtils.makePacketSocketAddress( + ETH_P_IPV6, mListenIfaceParams.index); + Os.bind(mFd, bindAddress); + } catch (ErrnoException | SocketException e) { + Log.wtf(mTag, "Failed to create socket", e); + closeSocketQuietly(mFd); + return null; + } + + return mFd; + } + + private Inet6Address getIpv6DestinationAddress(byte[] recvbuf) { + Inet6Address dstAddr; + try { + dstAddr = (Inet6Address) Inet6Address.getByAddress(Arrays.copyOfRange(recvbuf, + IPV6_DST_ADDR_OFFSET, IPV6_DST_ADDR_OFFSET + IPV6_ADDR_LEN)); + } catch (UnknownHostException | ClassCastException impossible) { + throw new AssertionError("16-byte array not valid IPv6 address?"); + } + return dstAddr; + } + + @Override + protected void handlePacket(byte[] recvbuf, int length) { + if (mSendIfaceParams == null) { + return; + } + + // The BPF filter should already have checked the length of the packet, but... + if (length < IPV6_HEADER_LEN) { + return; + } + Inet6Address destv6 = getIpv6DestinationAddress(recvbuf); + if (!destv6.isMulticastAddress()) { + return; + } + InetSocketAddress dest = new InetSocketAddress(destv6, 0); + + FileDescriptor fd = null; + try { + fd = Os.socket(AF_INET6, SOCK_RAW | SOCK_NONBLOCK, IPPROTO_RAW); + SocketUtils.bindSocketToInterface(fd, mSendIfaceParams.name); + + int ret = Os.sendto(fd, recvbuf, 0, length, 0, dest); + } catch (ErrnoException | SocketException e) { + Log.e(mTag, "handlePacket error: " + e); + } finally { + closeSocketQuietly(fd); + } + } +} diff --git a/packages/Tethering/src/android/net/ip/RouterAdvertisementDaemon.java b/packages/Tethering/src/android/net/ip/RouterAdvertisementDaemon.java index 6f017dcb623f..7c0b7cc7515c 100644 --- a/packages/Tethering/src/android/net/ip/RouterAdvertisementDaemon.java +++ b/packages/Tethering/src/android/net/ip/RouterAdvertisementDaemon.java @@ -18,6 +18,7 @@ package android.net.ip; import static android.net.util.NetworkConstants.IPV6_MIN_MTU; import static android.net.util.NetworkConstants.RFC7421_PREFIX_LENGTH; +import static android.net.util.TetheringUtils.getAllNodesForScopeId; import static android.system.OsConstants.AF_INET6; import static android.system.OsConstants.IPPROTO_ICMPV6; import static android.system.OsConstants.SOCK_RAW; @@ -44,7 +45,6 @@ import java.net.Inet6Address; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.SocketException; -import java.net.UnknownHostException; import java.nio.BufferOverflowException; import java.nio.ByteBuffer; import java.nio.ByteOrder; @@ -92,10 +92,6 @@ public class RouterAdvertisementDaemon { private static final int DAY_IN_SECONDS = 86_400; - private static final byte[] ALL_NODES = new byte[] { - (byte) 0xff, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 - }; - private final InterfaceParams mInterface; private final InetSocketAddress mAllNodes; @@ -240,7 +236,6 @@ public class RouterAdvertisementDaemon { } } - public RouterAdvertisementDaemon(InterfaceParams ifParams) { mInterface = ifParams; mAllNodes = new InetSocketAddress(getAllNodesForScopeId(mInterface.index), 0); @@ -363,15 +358,6 @@ public class RouterAdvertisementDaemon { } } - private static Inet6Address getAllNodesForScopeId(int scopeId) { - try { - return Inet6Address.getByAddress("ff02::1", ALL_NODES, scopeId); - } catch (UnknownHostException uhe) { - Log.wtf(TAG, "Failed to construct ff02::1 InetAddress: " + uhe); - return null; - } - } - private static byte asByte(int value) { return (byte) value; } diff --git a/packages/Tethering/src/android/net/util/TetheringUtils.java b/packages/Tethering/src/android/net/util/TetheringUtils.java index b17b4ba77cfb..53b54f7de05d 100644 --- a/packages/Tethering/src/android/net/util/TetheringUtils.java +++ b/packages/Tethering/src/android/net/util/TetheringUtils.java @@ -17,11 +17,15 @@ package android.net.util; import android.net.TetherStatsParcel; import android.net.TetheringRequestParcel; +import android.util.Log; import androidx.annotation.NonNull; import java.io.FileDescriptor; +import java.net.Inet6Address; import java.net.SocketException; +import java.net.UnknownHostException; +import java.util.Arrays; import java.util.Objects; /** @@ -30,6 +34,24 @@ import java.util.Objects; * {@hide} */ public class TetheringUtils { + public static final byte[] ALL_NODES = new byte[] { + (byte) 0xff, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 + }; + + /** + * Configures a socket for receiving and sending ICMPv6 neighbor advertisments. + * @param fd the socket's {@link FileDescriptor}. + */ + public static native void setupNaSocket(FileDescriptor fd) + throws SocketException; + + /** + * Configures a socket for receiving and sending ICMPv6 neighbor solicitations. + * @param fd the socket's {@link FileDescriptor}. + */ + public static native void setupNsSocket(FileDescriptor fd) + throws SocketException; + /** * The object which records offload Tx/Rx forwarded bytes/packets. * TODO: Replace the inner class ForwardedStats of class OffloadHardwareInterface with @@ -129,4 +151,15 @@ public class TetheringUtils { && request.exemptFromEntitlementCheck == otherRequest.exemptFromEntitlementCheck && request.showProvisioningUi == otherRequest.showProvisioningUi; } + + /** Get inet6 address for all nodes given scope ID. */ + public static Inet6Address getAllNodesForScopeId(int scopeId) { + try { + return Inet6Address.getByAddress("ff02::1", ALL_NODES, scopeId); + } catch (UnknownHostException uhe) { + Log.wtf("TetheringUtils", "Failed to construct Inet6Address from " + + Arrays.toString(ALL_NODES) + " and scopedId " + scopeId); + return null; + } + } } diff --git a/packages/Tethering/tests/privileged/Android.bp b/packages/Tethering/tests/privileged/Android.bp index a0fb24603a93..9217345dc2f5 100644 --- a/packages/Tethering/tests/privileged/Android.bp +++ b/packages/Tethering/tests/privileged/Android.bp @@ -14,8 +14,22 @@ // limitations under the License. // +java_defaults { + name: "TetheringPrivilegedTestsJniDefaults", + jni_libs: [ + "libdexmakerjvmtiagent", + "libstaticjvmtiagent", + "libtetherutilsjni", + ], + jni_uses_sdk_apis: true, + visibility: ["//visibility:private"], +} + android_test { name: "TetheringPrivilegedTests", + defaults: [ + "TetheringPrivilegedTestsJniDefaults", + ], srcs: [ "src/**/*.java", "src/**/*.kt", @@ -23,8 +37,13 @@ android_test { certificate: "networkstack", platform_apis: true, test_suites: [ - "general-tests", + "device-tests", "mts", ], + static_libs: [ + "androidx.test.rules", + "net-tests-utils", + "TetheringApiCurrentLib", + ], compile_multilib: "both", } diff --git a/packages/Tethering/tests/privileged/src/android/net/ip/DadProxyTest.java b/packages/Tethering/tests/privileged/src/android/net/ip/DadProxyTest.java new file mode 100644 index 000000000000..747d3e803026 --- /dev/null +++ b/packages/Tethering/tests/privileged/src/android/net/ip/DadProxyTest.java @@ -0,0 +1,338 @@ +/* + * 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.net.ip; + +import static android.system.OsConstants.IPPROTO_ICMPV6; +import static android.system.OsConstants.IPPROTO_TCP; + +import static com.android.internal.util.BitUtils.uint16; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import android.app.Instrumentation; +import android.content.Context; +import android.net.INetd; +import android.net.InetAddresses; +import android.net.MacAddress; +import android.net.TestNetworkInterface; +import android.net.TestNetworkManager; +import android.net.util.InterfaceParams; +import android.net.util.IpUtils; +import android.net.util.TetheringUtils; +import android.os.Handler; +import android.os.HandlerThread; +import android.os.IBinder; +import android.os.Looper; +import android.system.ErrnoException; +import android.system.Os; + +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import com.android.testutils.TapPacketReader; + +import org.junit.After; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.MockitoAnnotations; + +import java.io.FileDescriptor; +import java.nio.ByteBuffer; +import java.util.concurrent.atomic.AtomicReference; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public class DadProxyTest { + private static final int DATA_BUFFER_LEN = 4096; + private static final int PACKET_TIMEOUT_MS = 5_000; + + // TODO: make NetworkStackConstants accessible to this test and use the constant from there. + private static final int ETHER_SRC_ADDR_OFFSET = 6; + + private DadProxy mProxy; + TestNetworkInterface mUpstreamTestIface, mTetheredTestIface; + private InterfaceParams mUpstreamParams, mTetheredParams; + private HandlerThread mHandlerThread; + private Handler mHandler; + private TapPacketReader mUpstreamPacketReader, mTetheredPacketReader; + private FileDescriptor mUpstreamTapFd, mTetheredTapFd; + + private static INetd sNetd; + + @BeforeClass + public static void setupOnce() { + System.loadLibrary("tetherutilsjni"); + + final Instrumentation inst = InstrumentationRegistry.getInstrumentation(); + final IBinder netdIBinder = + (IBinder) inst.getContext().getSystemService(Context.NETD_SERVICE); + sNetd = INetd.Stub.asInterface(netdIBinder); + } + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + + mHandlerThread = new HandlerThread(getClass().getSimpleName()); + mHandlerThread.start(); + mHandler = new Handler(mHandlerThread.getLooper()); + + setupTapInterfaces(); + + // Looper must be prepared here since AndroidJUnitRunner runs tests on separate threads. + if (Looper.myLooper() == null) Looper.prepare(); + + DadProxy mProxy = setupProxy(); + } + + @After + public void tearDown() throws Exception { + if (mHandlerThread != null) { + mHandler.post(mUpstreamPacketReader::stop); // Also closes the socket + mHandler.post(mTetheredPacketReader::stop); // Also closes the socket + mUpstreamTapFd = null; + mTetheredTapFd = null; + mHandlerThread.quitSafely(); + } + + if (mTetheredParams != null) { + sNetd.networkRemoveInterface(INetd.LOCAL_NET_ID, mTetheredParams.name); + } + if (mUpstreamParams != null) { + sNetd.networkRemoveInterface(INetd.LOCAL_NET_ID, mUpstreamParams.name); + } + + if (mUpstreamTestIface != null) { + try { + Os.close(mUpstreamTestIface.getFileDescriptor().getFileDescriptor()); + } catch (ErrnoException e) { } + } + + if (mTetheredTestIface != null) { + try { + Os.close(mTetheredTestIface.getFileDescriptor().getFileDescriptor()); + } catch (ErrnoException e) { } + } + } + + private TestNetworkInterface setupTapInterface() { + final Instrumentation inst = InstrumentationRegistry.getInstrumentation(); + AtomicReference<TestNetworkInterface> iface = new AtomicReference<>(); + + inst.getUiAutomation().adoptShellPermissionIdentity(); + try { + final TestNetworkManager tnm = (TestNetworkManager) inst.getContext().getSystemService( + Context.TEST_NETWORK_SERVICE); + iface.set(tnm.createTapInterface()); + } finally { + inst.getUiAutomation().dropShellPermissionIdentity(); + } + + return iface.get(); + } + + private void setupTapInterfaces() { + // Create upstream test iface. + mUpstreamTestIface = setupTapInterface(); + mUpstreamParams = InterfaceParams.getByName(mUpstreamTestIface.getInterfaceName()); + assertNotNull(mUpstreamParams); + mUpstreamTapFd = mUpstreamTestIface.getFileDescriptor().getFileDescriptor(); + mUpstreamPacketReader = new TapPacketReader(mHandler, mUpstreamTapFd, + DATA_BUFFER_LEN); + mHandler.post(mUpstreamPacketReader::start); + + // Create tethered test iface. + mTetheredTestIface = setupTapInterface(); + mTetheredParams = InterfaceParams.getByName(mTetheredTestIface.getInterfaceName()); + assertNotNull(mTetheredParams); + mTetheredTapFd = mTetheredTestIface.getFileDescriptor().getFileDescriptor(); + mTetheredPacketReader = new TapPacketReader(mHandler, mTetheredTapFd, + DATA_BUFFER_LEN); + mHandler.post(mTetheredPacketReader::start); + } + + private static final int IPV6_HEADER_LEN = 40; + private static final int ETH_HEADER_LEN = 14; + private static final int ICMPV6_NA_NS_LEN = 24; + private static final int LL_TARGET_OPTION_LEN = 8; + private static final int ICMPV6_CHECKSUM_OFFSET = 2; + private static final int ETHER_TYPE_IPV6 = 0x86dd; + + // TODO: move the IpUtils code to frameworks/lib/net and link it statically. + private static int checksumFold(int sum) { + while (sum > 0xffff) { + sum = (sum >> 16) + (sum & 0xffff); + } + return sum; + } + + // TODO: move the IpUtils code to frameworks/lib/net and link it statically. + private static short checksumAdjust(short checksum, short oldWord, short newWord) { + checksum = (short) ~checksum; + int tempSum = checksumFold(uint16(checksum) + uint16(newWord) + 0xffff - uint16(oldWord)); + return (short) ~tempSum; + } + + // TODO: move the IpUtils code to frameworks/lib/net and link it statically. + private static short icmpv6Checksum(ByteBuffer buf, int ipOffset, int transportOffset, + int transportLen) { + // The ICMPv6 checksum is the same as the TCP checksum, except the pseudo-header uses + // 58 (ICMPv6) instead of 6 (TCP). Calculate the TCP checksum, and then do an incremental + // checksum adjustment for the change in the next header byte. + short checksum = IpUtils.tcpChecksum(buf, ipOffset, transportOffset, transportLen); + return checksumAdjust(checksum, (short) IPPROTO_TCP, (short) IPPROTO_ICMPV6); + } + + private static ByteBuffer createDadPacket(int type) { + // Refer to buildArpPacket() + int icmpLen = ICMPV6_NA_NS_LEN + + (type == NeighborPacketForwarder.ICMPV6_NEIGHBOR_ADVERTISEMENT + ? LL_TARGET_OPTION_LEN : 0); + final ByteBuffer buf = ByteBuffer.allocate(icmpLen + IPV6_HEADER_LEN + ETH_HEADER_LEN); + + // Ethernet header. + final MacAddress srcMac = MacAddress.fromString("33:33:ff:66:77:88"); + buf.put(srcMac.toByteArray()); + final MacAddress dstMac = MacAddress.fromString("01:02:03:04:05:06"); + buf.put(dstMac.toByteArray()); + buf.putShort((short) ETHER_TYPE_IPV6); + + // IPv6 header + byte[] version = {(byte) 0x60, 0x00, 0x00, 0x00}; + buf.put(version); // Version + buf.putShort((byte) icmpLen); // Length + buf.put((byte) IPPROTO_ICMPV6); // Next header + buf.put((byte) 0xff); // Hop limit + + final byte[] target = + InetAddresses.parseNumericAddress("fe80::1122:3344:5566:7788").getAddress(); + final byte[] src; + final byte[] dst; + if (type == NeighborPacketForwarder.ICMPV6_NEIGHBOR_SOLICITATION) { + src = InetAddresses.parseNumericAddress("::").getAddress(); + dst = InetAddresses.parseNumericAddress("ff02::1:ff66:7788").getAddress(); + } else { + src = target; + dst = TetheringUtils.ALL_NODES; + } + buf.put(src); + buf.put(dst); + + // ICMPv6 Header + buf.put((byte) type); // Type + buf.put((byte) 0x00); // Code + buf.putShort((short) 0); // Checksum + buf.putInt(0); // Reserved + buf.put(target); + + if (type == NeighborPacketForwarder.ICMPV6_NEIGHBOR_ADVERTISEMENT) { + //NA packet has LL target address + //ICMPv6 Option + buf.put((byte) 0x02); // Type + buf.put((byte) 0x01); // Length + byte[] ll_target = MacAddress.fromString("01:02:03:04:05:06").toByteArray(); + buf.put(ll_target); + } + + // Populate checksum field + final int transportOffset = ETH_HEADER_LEN + IPV6_HEADER_LEN; + final short checksum = icmpv6Checksum(buf, ETH_HEADER_LEN, transportOffset, icmpLen); + buf.putShort(transportOffset + ICMPV6_CHECKSUM_OFFSET, checksum); + + buf.flip(); + return buf; + } + + private DadProxy setupProxy() throws Exception { + DadProxy proxy = new DadProxy(mHandler, mTetheredParams); + mHandler.post(() -> proxy.setUpstreamIface(mUpstreamParams)); + + // Upstream iface is added to local network to simplify test case. + // Otherwise the test needs to create and destroy a network for the upstream iface. + sNetd.networkAddInterface(INetd.LOCAL_NET_ID, mUpstreamParams.name); + sNetd.networkAddInterface(INetd.LOCAL_NET_ID, mTetheredParams.name); + + return proxy; + } + + // TODO: change to assert. + private boolean waitForPacket(ByteBuffer packet, TapPacketReader reader) { + byte[] p; + + while ((p = reader.popPacket(PACKET_TIMEOUT_MS)) != null) { + final ByteBuffer buffer = ByteBuffer.wrap(p); + + if (buffer.compareTo(packet) == 0) return true; + } + return false; + } + + private void updateDstMac(ByteBuffer buf, MacAddress mac) { + buf.put(mac.toByteArray()); + buf.rewind(); + } + private void updateSrcMac(ByteBuffer buf, InterfaceParams ifaceParams) { + buf.position(ETHER_SRC_ADDR_OFFSET); + buf.put(ifaceParams.macAddr.toByteArray()); + buf.rewind(); + } + + @Test + public void testNaForwardingFromUpstreamToTether() throws Exception { + ByteBuffer na = createDadPacket(NeighborPacketForwarder.ICMPV6_NEIGHBOR_ADVERTISEMENT); + + mUpstreamPacketReader.sendResponse(na); + updateDstMac(na, MacAddress.fromString("33:33:00:00:00:01")); + updateSrcMac(na, mTetheredParams); + assertTrue(waitForPacket(na, mTetheredPacketReader)); + } + + @Test + // TODO: remove test once DAD works in both directions. + public void testNaForwardingFromTetherToUpstream() throws Exception { + ByteBuffer na = createDadPacket(NeighborPacketForwarder.ICMPV6_NEIGHBOR_ADVERTISEMENT); + + mTetheredPacketReader.sendResponse(na); + updateDstMac(na, MacAddress.fromString("33:33:00:00:00:01")); + updateSrcMac(na, mTetheredParams); + assertFalse(waitForPacket(na, mUpstreamPacketReader)); + } + + @Test + public void testNsForwardingFromTetherToUpstream() throws Exception { + ByteBuffer ns = createDadPacket(NeighborPacketForwarder.ICMPV6_NEIGHBOR_SOLICITATION); + + mTetheredPacketReader.sendResponse(ns); + updateSrcMac(ns, mUpstreamParams); + assertTrue(waitForPacket(ns, mUpstreamPacketReader)); + } + + @Test + // TODO: remove test once DAD works in both directions. + public void testNsForwardingFromUpstreamToTether() throws Exception { + ByteBuffer ns = createDadPacket(NeighborPacketForwarder.ICMPV6_NEIGHBOR_SOLICITATION); + + mUpstreamPacketReader.sendResponse(ns); + updateSrcMac(ns, mUpstreamParams); + assertFalse(waitForPacket(ns, mTetheredPacketReader)); + } +} diff --git a/packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java b/packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java index 3b72b5b47191..1a976adc60e0 100644 --- a/packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java +++ b/packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java @@ -86,6 +86,7 @@ import android.net.util.InterfaceParams; import android.net.util.InterfaceSet; import android.net.util.PrefixUtils; import android.net.util.SharedLog; +import android.os.Build; import android.os.Handler; import android.os.RemoteException; import android.os.test.TestLooper; @@ -100,8 +101,12 @@ import com.android.networkstack.tethering.BpfCoordinator; import com.android.networkstack.tethering.BpfCoordinator.Ipv6ForwardingRule; import com.android.networkstack.tethering.PrivateAddressCoordinator; import com.android.networkstack.tethering.TetheringConfiguration; +import com.android.testutils.DevSdkIgnoreRule; +import com.android.testutils.DevSdkIgnoreRule.IgnoreAfter; +import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; @@ -120,6 +125,9 @@ import java.util.List; @RunWith(AndroidJUnit4.class) @SmallTest public class IpServerTest { + @Rule + public final DevSdkIgnoreRule mIgnoreRule = new DevSdkIgnoreRule(); + private static final String IFACE_NAME = "testnet1"; private static final String UPSTREAM_IFACE = "upstream0"; private static final String UPSTREAM_IFACE2 = "upstream1"; @@ -132,6 +140,11 @@ public class IpServerTest { private static final InterfaceParams TEST_IFACE_PARAMS = new InterfaceParams( IFACE_NAME, 42 /* index */, MacAddress.ALL_ZEROS_ADDRESS, 1500 /* defaultMtu */); + private static final InterfaceParams UPSTREAM_IFACE_PARAMS = new InterfaceParams( + UPSTREAM_IFACE, UPSTREAM_IFINDEX, MacAddress.ALL_ZEROS_ADDRESS, 1500 /* defaultMtu */); + private static final InterfaceParams UPSTREAM_IFACE_PARAMS2 = new InterfaceParams( + UPSTREAM_IFACE2, UPSTREAM_IFINDEX2, MacAddress.ALL_ZEROS_ADDRESS, + 1500 /* defaultMtu */); private static final int MAKE_DHCPSERVER_TIMEOUT_MS = 1000; @@ -142,6 +155,7 @@ public class IpServerTest { @Mock private IpServer.Callback mCallback; @Mock private SharedLog mSharedLog; @Mock private IDhcpServer mDhcpServer; + @Mock private DadProxy mDadProxy; @Mock private RouterAdvertisementDaemon mRaDaemon; @Mock private IpNeighborMonitor mIpNeighborMonitor; @Mock private IpServer.Dependencies mDependencies; @@ -165,8 +179,11 @@ public class IpServerTest { private void initStateMachine(int interfaceType, boolean usingLegacyDhcp, boolean usingBpfOffload) throws Exception { + when(mDependencies.getDadProxy(any(), any())).thenReturn(mDadProxy); when(mDependencies.getRouterAdvertisementDaemon(any())).thenReturn(mRaDaemon); when(mDependencies.getInterfaceParams(IFACE_NAME)).thenReturn(TEST_IFACE_PARAMS); + when(mDependencies.getInterfaceParams(UPSTREAM_IFACE)).thenReturn(UPSTREAM_IFACE_PARAMS); + when(mDependencies.getInterfaceParams(UPSTREAM_IFACE2)).thenReturn(UPSTREAM_IFACE_PARAMS2); when(mDependencies.getIfindex(eq(UPSTREAM_IFACE))).thenReturn(UPSTREAM_IFINDEX); when(mDependencies.getIfindex(eq(UPSTREAM_IFACE2))).thenReturn(UPSTREAM_IFINDEX2); @@ -1103,4 +1120,78 @@ public class IpServerTest { } return true; } + + @Test @IgnoreUpTo(Build.VERSION_CODES.R) + public void dadProxyUpdates() throws Exception { + InOrder inOrder = inOrder(mDadProxy); + initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE); + inOrder.verify(mDadProxy).setUpstreamIface(UPSTREAM_IFACE_PARAMS); + + // Add an upstream without IPv6. + dispatchTetherConnectionChanged(UPSTREAM_IFACE, null, 0); + inOrder.verify(mDadProxy).setUpstreamIface(null); + + // Add IPv6 to the upstream. + LinkProperties lp = new LinkProperties(); + lp.setInterfaceName(UPSTREAM_IFACE); + dispatchTetherConnectionChanged(UPSTREAM_IFACE, lp, 0); + inOrder.verify(mDadProxy).setUpstreamIface(UPSTREAM_IFACE_PARAMS); + + // Change upstream. + // New linkproperties is needed, otherwise changing the iface has no impact. + LinkProperties lp2 = new LinkProperties(); + lp2.setInterfaceName(UPSTREAM_IFACE2); + dispatchTetherConnectionChanged(UPSTREAM_IFACE2, lp2, 0); + inOrder.verify(mDadProxy).setUpstreamIface(UPSTREAM_IFACE_PARAMS2); + + // Lose IPv6 on the upstream... + dispatchTetherConnectionChanged(UPSTREAM_IFACE2, null, 0); + inOrder.verify(mDadProxy).setUpstreamIface(null); + + // ... and regain it on a different upstream. + dispatchTetherConnectionChanged(UPSTREAM_IFACE, lp, 0); + inOrder.verify(mDadProxy).setUpstreamIface(UPSTREAM_IFACE_PARAMS); + + // Lose upstream. + dispatchTetherConnectionChanged(null, null, 0); + inOrder.verify(mDadProxy).setUpstreamIface(null); + + // Regain upstream. + dispatchTetherConnectionChanged(UPSTREAM_IFACE, lp, 0); + inOrder.verify(mDadProxy).setUpstreamIface(UPSTREAM_IFACE_PARAMS); + + // Stop tethering. + mIpServer.stop(); + mLooper.dispatchAll(); + } + + private void checkDadProxyEnabled(boolean expectEnabled) throws Exception { + initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE); + InOrder inOrder = inOrder(mDadProxy); + // Add IPv6 to the upstream. + LinkProperties lp = new LinkProperties(); + lp.setInterfaceName(UPSTREAM_IFACE); + if (expectEnabled) { + inOrder.verify(mDadProxy).setUpstreamIface(UPSTREAM_IFACE_PARAMS); + } else { + inOrder.verifyNoMoreInteractions(); + } + // Stop tethering. + mIpServer.stop(); + mLooper.dispatchAll(); + if (expectEnabled) { + inOrder.verify(mDadProxy).stop(); + } + else { + verify(mDependencies, never()).getDadProxy(any(), any()); + } + } + @Test @IgnoreAfter(Build.VERSION_CODES.R) + public void testDadProxyUpdates_DisabledUpToR() throws Exception { + checkDadProxyEnabled(false); + } + @Test @IgnoreUpTo(Build.VERSION_CODES.R) + public void testDadProxyUpdates_EnabledAfterR() throws Exception { + checkDadProxyEnabled(true); + } } diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java index 1fe3840b51a8..df570206e389 100644 --- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java +++ b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java @@ -110,6 +110,7 @@ import android.net.TetheringRequestParcel; import android.net.dhcp.DhcpServerCallbacks; import android.net.dhcp.DhcpServingParamsParcel; import android.net.dhcp.IDhcpServer; +import android.net.ip.DadProxy; import android.net.ip.IpNeighborMonitor; import android.net.ip.IpServer; import android.net.ip.RouterAdvertisementDaemon; @@ -196,6 +197,7 @@ public class TetheringTest { @Mock private CarrierConfigManager mCarrierConfigManager; @Mock private UpstreamNetworkMonitor mUpstreamNetworkMonitor; @Mock private IPv6TetheringCoordinator mIPv6TetheringCoordinator; + @Mock private DadProxy mDadProxy; @Mock private RouterAdvertisementDaemon mRouterAdvertisementDaemon; @Mock private IpNeighborMonitor mIpNeighborMonitor; @Mock private IDhcpServer mDhcpServer; @@ -280,6 +282,12 @@ public class TetheringTest { public class MockIpServerDependencies extends IpServer.Dependencies { @Override + public DadProxy getDadProxy( + Handler handler, InterfaceParams ifParams) { + return mDadProxy; + } + + @Override public RouterAdvertisementDaemon getRouterAdvertisementDaemon( InterfaceParams ifParams) { return mRouterAdvertisementDaemon; @@ -832,6 +840,7 @@ public class TetheringTest { verify(mNetd, times(1)).ipfwdAddInterfaceForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); sendIPv6TetherUpdates(upstreamState); + verify(mDadProxy, never()).setUpstreamIface(notNull()); verify(mRouterAdvertisementDaemon, never()).buildNewRa(any(), notNull()); verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).startWithCallbacks( any(), any()); @@ -858,6 +867,8 @@ public class TetheringTest { verify(mNetd, times(1)).ipfwdAddInterfaceForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); sendIPv6TetherUpdates(upstreamState); + // TODO: add interfaceParams to compare in verify. + verify(mDadProxy, times(1)).setUpstreamIface(notNull()); verify(mRouterAdvertisementDaemon, times(1)).buildNewRa(any(), notNull()); verify(mNetd, times(1)).tetherApplyDnsInterfaces(); } @@ -874,6 +885,7 @@ public class TetheringTest { any(), any()); sendIPv6TetherUpdates(upstreamState); + verify(mDadProxy, times(1)).setUpstreamIface(notNull()); verify(mRouterAdvertisementDaemon, times(1)).buildNewRa(any(), notNull()); verify(mNetd, times(1)).tetherApplyDnsInterfaces(); } @@ -891,6 +903,7 @@ public class TetheringTest { verify(mNetd, times(1)).ipfwdAddInterfaceForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); sendIPv6TetherUpdates(upstreamState); + verify(mDadProxy, times(1)).setUpstreamIface(notNull()); verify(mRouterAdvertisementDaemon, times(1)).buildNewRa(any(), notNull()); verify(mNetd, times(1)).tetherApplyDnsInterfaces(); } diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java index 9495fb5fdc77..ff794691d2b4 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java @@ -730,7 +730,8 @@ public class AccessibilityWindowManager { case WindowManager.LayoutParams.TYPE_SYSTEM_ERROR: case WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY: case WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY: - case WindowManager.LayoutParams.TYPE_SCREENSHOT: { + case WindowManager.LayoutParams.TYPE_SCREENSHOT: + case WindowManager.LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY: { return AccessibilityWindowInfo.TYPE_SYSTEM; } diff --git a/services/core/java/android/os/BatteryStatsInternal.java b/services/core/java/android/os/BatteryStatsInternal.java index b7fed87d570d..958c15c8d432 100644 --- a/services/core/java/android/os/BatteryStatsInternal.java +++ b/services/core/java/android/os/BatteryStatsInternal.java @@ -50,5 +50,10 @@ public abstract class BatteryStatsInternal { * Informs battery stats of binder stats for the given work source UID. */ public abstract void noteBinderCallStats(int workSourceUid, long incrementalBinderCallCount, - Collection<BinderCallsStats.CallStat> callStats, int[] binderThreadNativeTids); + Collection<BinderCallsStats.CallStat> callStats); + + /** + * Informs battery stats of native thread IDs of threads taking incoming binder calls. + */ + public abstract void noteBinderThreadNativeIds(int[] binderThreadNativeTids); } diff --git a/services/core/java/com/android/server/BinderCallsStatsService.java b/services/core/java/com/android/server/BinderCallsStatsService.java index c9513592ea79..66ac889ff2ca 100644 --- a/services/core/java/com/android/server/BinderCallsStatsService.java +++ b/services/core/java/com/android/server/BinderCallsStatsService.java @@ -49,6 +49,7 @@ import com.android.internal.util.DumpUtils; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.Collection; import java.util.List; public class BinderCallsStatsService extends Binder { @@ -273,7 +274,19 @@ public class BinderCallsStatsService extends Binder { BatteryStatsInternal batteryStatsInternal = getLocalService( BatteryStatsInternal.class); - mBinderCallsStats.setCallStatsObserver(batteryStatsInternal::noteBinderCallStats); + mBinderCallsStats.setCallStatsObserver(new BinderInternal.CallStatsObserver() { + @Override + public void noteCallStats(int workSourceUid, long incrementalCallCount, + Collection<BinderCallsStats.CallStat> callStats) { + batteryStatsInternal.noteBinderCallStats(workSourceUid, + incrementalCallCount, callStats); + } + + @Override + public void noteBinderThreadNativeIds(int[] binderThreadNativeTids) { + batteryStatsInternal.noteBinderThreadNativeIds(binderThreadNativeTids); + } + }); // It needs to be called before mService.systemReady to make sure the observer is // initialized before installing it. diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java index e342c1fcf114..6d774869b391 100644 --- a/services/core/java/com/android/server/StorageManagerService.java +++ b/services/core/java/com/android/server/StorageManagerService.java @@ -4641,6 +4641,11 @@ class StorageManagerService extends IStorageManager.Stub // make sure the OBB dir for the application is setup correctly, if it exists. File[] packageObbDirs = userEnv.buildExternalStorageAppObbDirs(packageName); for (File packageObbDir : packageObbDirs) { + if (packageObbDir.getPath().startsWith( + Environment.getDataPreloadsMediaDirectory().getPath())) { + Slog.i(TAG, "Skipping app data preparation for " + packageObbDir); + continue; + } try { mVold.fixupAppDir(packageObbDir.getCanonicalPath() + "/", uid); } catch (IOException e) { diff --git a/services/core/java/com/android/server/UiModeManagerService.java b/services/core/java/com/android/server/UiModeManagerService.java index bcb7bfacfaf3..0135b9d2f8ab 100644 --- a/services/core/java/com/android/server/UiModeManagerService.java +++ b/services/core/java/com/android/server/UiModeManagerService.java @@ -72,7 +72,6 @@ import com.android.internal.app.DisableCarModeActivity; import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; import com.android.internal.notification.SystemNotificationChannels; import com.android.internal.util.DumpUtils; -import com.android.server.SystemService.TargetUser; import com.android.server.twilight.TwilightListener; import com.android.server.twilight.TwilightManager; import com.android.server.twilight.TwilightState; @@ -327,8 +326,16 @@ final class UiModeManagerService extends SystemService { @Override public void onUserSwitching(@Nullable TargetUser from, @NonNull TargetUser to) { mCurrentUser = to.getUserIdentifier(); + if (mNightMode == MODE_NIGHT_AUTO) persistComputedNightMode(from.getUserIdentifier()); getContext().getContentResolver().unregisterContentObserver(mSetupWizardObserver); verifySetupWizardCompleted(); + synchronized (mLock) { + // only update if the value is actually changed + if (updateNightModeFromSettingsLocked(getContext(), getContext().getResources(), + to.getUserIdentifier())) { + updateLocked(0, 0); + } + } } @Override @@ -356,11 +363,10 @@ final class UiModeManagerService extends SystemService { new IntentFilter(Intent.ACTION_DOCK_EVENT)); IntentFilter batteryFilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED); context.registerReceiver(mBatteryReceiver, batteryFilter); - IntentFilter filter = new IntentFilter(); - filter.addAction(Intent.ACTION_USER_SWITCHED); context.registerReceiver(mSettingsRestored, new IntentFilter(Intent.ACTION_SETTING_RESTORED)); - context.registerReceiver(new UserSwitchedReceiver(), filter, null, mHandler); + context.registerReceiver(mOnShutdown, + new IntentFilter(Intent.ACTION_SHUTDOWN)); updateConfigurationLocked(); applyConfigurationExternallyLocked(); } @@ -407,6 +413,21 @@ final class UiModeManagerService extends SystemService { publishLocalService(UiModeManagerInternal.class, mLocalService); } + private final BroadcastReceiver mOnShutdown = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (mNightMode == MODE_NIGHT_AUTO) { + persistComputedNightMode(mCurrentUser); + } + } + }; + + private void persistComputedNightMode(int userId) { + Secure.putIntForUser(getContext().getContentResolver(), + Secure.UI_NIGHT_MODE_LAST_COMPUTED, mComputedNightMode ? 1 : 0, + userId); + } + private final BroadcastReceiver mSettingsRestored = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { @@ -508,6 +529,10 @@ final class UiModeManagerService extends SystemService { Secure.getLongForUser(context.getContentResolver(), Secure.DARK_THEME_CUSTOM_END_TIME, DEFAULT_CUSTOM_NIGHT_END_TIME.toNanoOfDay() / 1000L, userId) * 1000); + if (mNightMode == MODE_NIGHT_AUTO) { + mComputedNightMode = Secure.getIntForUser(context.getContentResolver(), + Secure.UI_NIGHT_MODE_LAST_COMPUTED, 0, userId) != 0; + } } return oldNightMode != mNightMode; @@ -1630,18 +1655,4 @@ final class UiModeManagerService extends SystemService { } } } - - private final class UserSwitchedReceiver extends BroadcastReceiver { - @Override - public void onReceive(Context context, Intent intent) { - synchronized (mLock) { - final int currentId = intent.getIntExtra( - Intent.EXTRA_USER_HANDLE, USER_SYSTEM); - // only update if the value is actually changed - if (updateNightModeFromSettingsLocked(context, context.getResources(), currentId)) { - updateLocked(0, 0); - } - } - } - } } diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index bfe04e628f81..c0f6011a45cd 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -482,13 +482,14 @@ public final class ActiveServices { String callingPackage, @Nullable String callingFeatureId, final int userId) throws TransactionTooLargeException { return startServiceLocked(caller, service, resolvedType, callingPid, callingUid, fgRequired, - hideFgNotification, callingPackage, callingFeatureId, userId, false); + hideFgNotification, callingPackage, callingFeatureId, userId, false, null); } ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType, int callingPid, int callingUid, boolean fgRequired, boolean hideFgNotification, String callingPackage, @Nullable String callingFeatureId, final int userId, - boolean allowBackgroundActivityStarts) throws TransactionTooLargeException { + boolean allowBackgroundActivityStarts, @Nullable IBinder backgroundActivityStartsToken) + throws TransactionTooLargeException { if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE, "startService: " + service + " type=" + resolvedType + " args=" + service.getExtras()); @@ -733,7 +734,7 @@ public final class ActiveServices { } } if (allowBackgroundActivityStarts) { - r.allowBgActivityStartsOnServiceStart(); + r.allowBgActivityStartsOnServiceStart(backgroundActivityStartsToken); } ComponentName cmp = startServiceInnerLocked(smap, service, r, callerFg, addToStarting); return cmp; @@ -2609,12 +2610,12 @@ public final class ActiveServices { private int getAllowMode(Intent service, @Nullable String callingPackage) { if (callingPackage == null || service.getComponent() == null) { - return ActivityManagerInternal.ALLOW_NON_FULL_IN_PROFILE_OR_FULL; + return ActivityManagerInternal.ALLOW_NON_FULL_IN_PROFILE; } if (callingPackage.equals(service.getComponent().getPackageName())) { - return ActivityManagerInternal.ALLOW_ACROSS_PROFILES_IN_PROFILE_OR_FULL; + return ActivityManagerInternal.ALLOW_ALL_PROFILE_PERMISSIONS_IN_PROFILE; } else { - return ActivityManagerInternal.ALLOW_NON_FULL_IN_PROFILE_OR_FULL; + return ActivityManagerInternal.ALLOW_NON_FULL_IN_PROFILE; } } diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 2de3e525d2da..bd7d3de9d200 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -16306,7 +16306,8 @@ public class ActivityManagerService extends IActivityManager.Stub try { res = mServices.startServiceLocked(null, service, resolvedType, -1, uid, fgRequired, false, callingPackage, - callingFeatureId, userId, allowBackgroundActivityStarts); + callingFeatureId, userId, allowBackgroundActivityStarts, + backgroundActivityStartsToken); } finally { Binder.restoreCallingIdentity(origId); } diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java index 808a6993af1a..76125aabc978 100644 --- a/services/core/java/com/android/server/am/BatteryStatsService.java +++ b/services/core/java/com/android/server/am/BatteryStatsService.java @@ -240,14 +240,20 @@ public final class BatteryStatsService extends IBatteryStats.Stub @Override public void noteBinderCallStats(int workSourceUid, long incrementatCallCount, - Collection<BinderCallsStats.CallStat> callStats, int[] binderThreadNativeTids) { + Collection<BinderCallsStats.CallStat> callStats) { synchronized (BatteryStatsService.this.mLock) { mHandler.sendMessage(PooledLambda.obtainMessage( - mStats::noteBinderCallStats, workSourceUid, incrementatCallCount, - callStats, binderThreadNativeTids, + mStats::noteBinderCallStats, workSourceUid, incrementatCallCount, callStats, SystemClock.elapsedRealtime(), SystemClock.uptimeMillis())); } } + + @Override + public void noteBinderThreadNativeIds(int[] binderThreadNativeTids) { + synchronized (BatteryStatsService.this.mLock) { + mStats.noteBinderThreadNativeIds(binderThreadNativeTids); + } + } } @Override diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java index 93105daa6c5d..57f811215e50 100644 --- a/services/core/java/com/android/server/am/BroadcastQueue.java +++ b/services/core/java/com/android/server/am/BroadcastQueue.java @@ -1686,7 +1686,7 @@ public final class BroadcastQueue { // that request - we don't want the token to be swept from under our feet... mHandler.removeCallbacksAndMessages(msgToken); // ...then add the token - proc.addAllowBackgroundActivityStartsToken(r, r.mBackgroundActivityStartsToken); + proc.addOrUpdateAllowBackgroundActivityStartsToken(r, r.mBackgroundActivityStartsToken); } final void setBroadcastTimeoutLocked(long timeoutTime) { diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java index f1e12c55df18..2dced8d704bb 100644 --- a/services/core/java/com/android/server/am/ProcessRecord.java +++ b/services/core/java/com/android/server/am/ProcessRecord.java @@ -77,6 +77,7 @@ import java.io.StringWriter; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Objects; import java.util.function.Consumer; /** @@ -1338,13 +1339,15 @@ class ProcessRecord implements WindowProcessListener { * {@param originatingToken} if you have one such originating token, this is useful for tracing * back the grant in the case of the notification token. */ - void addAllowBackgroundActivityStartsToken(Binder entity, @Nullable IBinder originatingToken) { - if (entity == null) return; - mWindowProcessController.addAllowBackgroundActivityStartsToken(entity, originatingToken); + void addOrUpdateAllowBackgroundActivityStartsToken(Binder entity, + @Nullable IBinder originatingToken) { + Objects.requireNonNull(entity); + mWindowProcessController.addOrUpdateAllowBackgroundActivityStartsToken(entity, + originatingToken); } void removeAllowBackgroundActivityStartsToken(Binder entity) { - if (entity == null) return; + Objects.requireNonNull(entity); mWindowProcessController.removeAllowBackgroundActivityStartsToken(entity); } diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java index 55e0f2ea5212..a7d8ca496f75 100644 --- a/services/core/java/com/android/server/am/ServiceRecord.java +++ b/services/core/java/com/android/server/am/ServiceRecord.java @@ -22,6 +22,7 @@ import static android.app.PendingIntent.FLAG_UPDATE_CURRENT; import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM; import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME; +import android.annotation.Nullable; import android.app.Notification; import android.app.PendingIntent; import android.content.ComponentName; @@ -139,6 +140,12 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN // used to clean up the state of mIsAllowedBgActivityStartsByStart after a timeout private Runnable mCleanUpAllowBgActivityStartsByStartCallback; private ProcessRecord mAppForAllowingBgActivityStartsByStart; + // These are the originating tokens that currently allow bg activity starts by service start. + // This is used to trace back the grant when starting activities. We only pass such token to the + // ProcessRecord if it's the *only* cause for bg activity starts exemption, otherwise we pass + // null. + @GuardedBy("ams") + private List<IBinder> mBgActivityStartsByStartOriginatingTokens = new ArrayList<>(); // allow while-in-use permissions in foreground service or not. // while-in-use permissions in FGS started from background might be restricted. @@ -588,7 +595,8 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN ? _proc : null; if (mIsAllowedBgActivityStartsByStart || mIsAllowedBgActivityStartsByBinding) { - _proc.addAllowBackgroundActivityStartsToken(this, null); + _proc.addOrUpdateAllowBackgroundActivityStartsToken(this, + getExclusiveOriginatingToken()); } else { _proc.removeAllowBackgroundActivityStartsToken(this); } @@ -679,10 +687,8 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN } void setAllowedBgActivityStartsByBinding(boolean newValue) { - if (mIsAllowedBgActivityStartsByBinding != newValue) { - mIsAllowedBgActivityStartsByBinding = newValue; - updateParentProcessBgActivityStartsToken(); - } + mIsAllowedBgActivityStartsByBinding = newValue; + updateParentProcessBgActivityStartsToken(); } /** @@ -691,7 +697,8 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN * timeout. Note that the ability for starting background activities persists for the process * even if the service is subsequently stopped. */ - void allowBgActivityStartsOnServiceStart() { + void allowBgActivityStartsOnServiceStart(@Nullable IBinder originatingToken) { + mBgActivityStartsByStartOriginatingTokens.add(originatingToken); setAllowedBgActivityStartsByStart(true); if (app != null) { mAppForAllowingBgActivityStartsByStart = app; @@ -701,32 +708,49 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN if (mCleanUpAllowBgActivityStartsByStartCallback == null) { mCleanUpAllowBgActivityStartsByStartCallback = () -> { synchronized (ams) { - if (app == mAppForAllowingBgActivityStartsByStart) { - // The process we allowed is still running the service. We remove - // the ability by start, but it may still be allowed via bound connections. - setAllowedBgActivityStartsByStart(false); - } else if (mAppForAllowingBgActivityStartsByStart != null) { - // The process we allowed is not running the service. It therefore can't be - // bound so we can unconditionally remove the ability. - mAppForAllowingBgActivityStartsByStart - .removeAllowBackgroundActivityStartsToken(ServiceRecord.this); + mBgActivityStartsByStartOriginatingTokens.remove(0); + if (!mBgActivityStartsByStartOriginatingTokens.isEmpty()) { + // There are other callbacks in the queue, let's just update the originating + // token + if (mIsAllowedBgActivityStartsByStart) { + mAppForAllowingBgActivityStartsByStart + .addOrUpdateAllowBackgroundActivityStartsToken( + this, getExclusiveOriginatingToken()); + } else { + Slog.wtf(TAG, + "Service callback to revoke bg activity starts by service " + + "start triggered but " + + "mIsAllowedBgActivityStartsByStart = false. This " + + "should never happen."); + } + } else { + // Last callback on the queue + if (app == mAppForAllowingBgActivityStartsByStart) { + // The process we allowed is still running the service. We remove + // the ability by start, but it may still be allowed via bound + // connections. + setAllowedBgActivityStartsByStart(false); + } else if (mAppForAllowingBgActivityStartsByStart != null) { + // The process we allowed is not running the service. It therefore can't + // be bound so we can unconditionally remove the ability. + mAppForAllowingBgActivityStartsByStart + .removeAllowBackgroundActivityStartsToken(ServiceRecord.this); + } + mAppForAllowingBgActivityStartsByStart = null; } - mAppForAllowingBgActivityStartsByStart = null; } }; } - // if there's a request pending from the past, drop it before scheduling a new one - ams.mHandler.removeCallbacks(mCleanUpAllowBgActivityStartsByStartCallback); + // Existing callbacks will only update the originating token, only when the last callback is + // executed is the grant revoked. ams.mHandler.postDelayed(mCleanUpAllowBgActivityStartsByStartCallback, ams.mConstants.SERVICE_BG_ACTIVITY_START_TIMEOUT); } private void setAllowedBgActivityStartsByStart(boolean newValue) { - if (mIsAllowedBgActivityStartsByStart != newValue) { - mIsAllowedBgActivityStartsByStart = newValue; - updateParentProcessBgActivityStartsToken(); - } + mIsAllowedBgActivityStartsByStart = newValue; + updateParentProcessBgActivityStartsToken(); } /** @@ -736,8 +760,8 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN * {@code mIsAllowedBgActivityStartsByBinding}. If either is true, this ServiceRecord * should be contributing as a token in parent ProcessRecord. * - * @see com.android.server.am.ProcessRecord#addAllowBackgroundActivityStartsToken(Binder, - * IBinder) + * @see com.android.server.am.ProcessRecord#addOrUpdateAllowBackgroundActivityStartsToken( + * Binder, IBinder) * @see com.android.server.am.ProcessRecord#removeAllowBackgroundActivityStartsToken(Binder) */ private void updateParentProcessBgActivityStartsToken() { @@ -747,12 +771,37 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN if (mIsAllowedBgActivityStartsByStart || mIsAllowedBgActivityStartsByBinding) { // if the token is already there it's safe to "re-add it" - we're dealing with // a set of Binder objects - app.addAllowBackgroundActivityStartsToken(this, null); + app.addOrUpdateAllowBackgroundActivityStartsToken(this, getExclusiveOriginatingToken()); } else { app.removeAllowBackgroundActivityStartsToken(this); } } + /** + * Returns the originating token if that's the only reason background activity starts are + * allowed. In order for that to happen the service has to be allowed only due to starts, since + * bindings are not associated with originating tokens, and all the start tokens have to be the + * same and there can't be any null originating token in the queue. + * + * Originating tokens are optional, so the caller could provide null when it allows bg activity + * starts. + */ + @Nullable + private IBinder getExclusiveOriginatingToken() { + if (mIsAllowedBgActivityStartsByBinding + || mBgActivityStartsByStartOriginatingTokens.isEmpty()) { + return null; + } + IBinder firstToken = mBgActivityStartsByStartOriginatingTokens.get(0); + for (int i = 1, n = mBgActivityStartsByStartOriginatingTokens.size(); i < n; i++) { + IBinder token = mBgActivityStartsByStartOriginatingTokens.get(i); + if (token != firstToken) { + return null; + } + } + return firstToken; + } + @GuardedBy("ams") void updateKeepWarmLocked() { mKeepWarming = ams.mConstants.KEEP_WARMING_SERVICES.contains(name) diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java index 3dfbcc71dd3c..eb60573e6f17 100644 --- a/services/core/java/com/android/server/am/UserController.java +++ b/services/core/java/com/android/server/am/UserController.java @@ -23,11 +23,10 @@ import static android.app.ActivityManager.USER_OP_ERROR_IS_SYSTEM; import static android.app.ActivityManager.USER_OP_ERROR_RELATED_USERS_CANNOT_STOP; import static android.app.ActivityManager.USER_OP_IS_CURRENT; import static android.app.ActivityManager.USER_OP_SUCCESS; -import static android.app.ActivityManagerInternal.ALLOW_ACROSS_PROFILES_IN_PROFILE_OR_FULL; -import static android.app.ActivityManagerInternal.ALLOW_ACROSS_PROFILES_IN_PROFILE_OR_NON_FULL; +import static android.app.ActivityManagerInternal.ALLOW_ALL_PROFILE_PERMISSIONS_IN_PROFILE; import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY; import static android.app.ActivityManagerInternal.ALLOW_NON_FULL; -import static android.app.ActivityManagerInternal.ALLOW_NON_FULL_IN_PROFILE_OR_FULL; +import static android.app.ActivityManagerInternal.ALLOW_NON_FULL_IN_PROFILE; import static android.os.Process.SHELL_UID; import static android.os.Process.SYSTEM_UID; @@ -1912,12 +1911,11 @@ class UserController implements Handler.Callback { callingUid, -1, true) != PackageManager.PERMISSION_GRANTED) { // If the caller does not have either permission, they are always doomed. allow = false; - } else if (allowMode == ALLOW_NON_FULL - || allowMode == ALLOW_ACROSS_PROFILES_IN_PROFILE_OR_NON_FULL) { + } else if (allowMode == ALLOW_NON_FULL) { // We are blanket allowing non-full access, you lucky caller! allow = true; - } else if (allowMode == ALLOW_NON_FULL_IN_PROFILE_OR_FULL - || allowMode == ALLOW_ACROSS_PROFILES_IN_PROFILE_OR_FULL) { + } else if (allowMode == ALLOW_NON_FULL_IN_PROFILE + || allowMode == ALLOW_ALL_PROFILE_PERMISSIONS_IN_PROFILE) { // We may or may not allow this depending on whether the two users are // in the same profile. allow = isSameProfileGroup; @@ -1944,15 +1942,12 @@ class UserController implements Handler.Callback { builder.append("; this requires "); builder.append(INTERACT_ACROSS_USERS_FULL); if (allowMode != ALLOW_FULL_ONLY) { - if (allowMode == ALLOW_NON_FULL - || allowMode == ALLOW_ACROSS_PROFILES_IN_PROFILE_OR_NON_FULL - || isSameProfileGroup) { + if (allowMode == ALLOW_NON_FULL || isSameProfileGroup) { builder.append(" or "); builder.append(INTERACT_ACROSS_USERS); } if (isSameProfileGroup - && (allowMode == ALLOW_ACROSS_PROFILES_IN_PROFILE_OR_FULL - || allowMode == ALLOW_ACROSS_PROFILES_IN_PROFILE_OR_NON_FULL)) { + && allowMode == ALLOW_ALL_PROFILE_PERMISSIONS_IN_PROFILE) { builder.append(" or "); builder.append(INTERACT_ACROSS_PROFILES); } @@ -1979,8 +1974,7 @@ class UserController implements Handler.Callback { private boolean canInteractWithAcrossProfilesPermission( int allowMode, boolean isSameProfileGroup, int callingPid, int callingUid, String callingPackage) { - if (allowMode != ALLOW_ACROSS_PROFILES_IN_PROFILE_OR_FULL - && allowMode != ALLOW_ACROSS_PROFILES_IN_PROFILE_OR_NON_FULL) { + if (allowMode != ALLOW_ALL_PROFILE_PERMISSIONS_IN_PROFILE) { return false; } if (!isSameProfileGroup) { diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java index 6099e52e54e9..668713f0fee7 100644 --- a/services/core/java/com/android/server/appop/AppOpsService.java +++ b/services/core/java/com/android/server/appop/AppOpsService.java @@ -19,7 +19,6 @@ package com.android.server.appop; import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_CAMERA; import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_LOCATION; import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_MICROPHONE; -import static android.app.ActivityManagerInternal.ALLOW_ACROSS_PROFILES_IN_PROFILE_OR_NON_FULL; import static android.app.AppOpsManager.CALL_BACK_ON_SWITCHED_OP; import static android.app.AppOpsManager.FILTER_BY_ATTRIBUTION_TAG; import static android.app.AppOpsManager.FILTER_BY_OP_NAMES; @@ -37,7 +36,6 @@ import static android.app.AppOpsManager.OP_CAMERA; import static android.app.AppOpsManager.OP_FLAGS_ALL; import static android.app.AppOpsManager.OP_FLAG_SELF; import static android.app.AppOpsManager.OP_FLAG_TRUSTED_PROXIED; -import static android.app.AppOpsManager.OP_INTERACT_ACROSS_PROFILES; import static android.app.AppOpsManager.OP_NONE; import static android.app.AppOpsManager.OP_PLAY_AUDIO; import static android.app.AppOpsManager.OP_RECORD_AUDIO; @@ -131,7 +129,6 @@ import android.provider.Settings; import android.util.ArrayMap; import android.util.ArraySet; import android.util.AtomicFile; -import android.util.EventLog; import android.util.KeyValueListParser; import android.util.LongSparseArray; import android.util.Pair; @@ -165,7 +162,6 @@ import com.android.server.LocalServices; import com.android.server.LockGuard; import com.android.server.SystemServerInitThreadPool; import com.android.server.SystemServiceManager; -import com.android.server.am.ActivityManagerService; import com.android.server.pm.PackageList; import com.android.server.pm.parsing.pkg.AndroidPackage; @@ -2203,11 +2199,8 @@ public class AppOpsService extends IAppOpsService.Stub { + " by uid " + Binder.getCallingUid()); } - int userId = UserHandle.getUserId(uid); - enforceManageAppOpsModes(Binder.getCallingPid(), Binder.getCallingUid(), uid); verifyIncomingOp(code); - verifyIncomingUser(userId); code = AppOpsManager.opToSwitch(code); if (permissionPolicyCallback == null) { @@ -2252,11 +2245,6 @@ public class AppOpsService extends IAppOpsService.Stub { scheduleWriteLocked(); } uidState.evalForegroundOps(mOpModeWatchers); - - if (code == OP_INTERACT_ACROSS_PROFILES) { - // Invalidate package info cache as the visibility of packages might have changed - PackageManager.invalidatePackageInfoCache(); - } } notifyOpChangedForAllPkgsInUid(code, uid, false, permissionPolicyCallback); @@ -2462,12 +2450,8 @@ public class AppOpsService extends IAppOpsService.Stub { private void setMode(int code, int uid, @NonNull String packageName, int mode, @Nullable IAppOpsCallback permissionPolicyCallback) { enforceManageAppOpsModes(Binder.getCallingPid(), Binder.getCallingUid(), uid); - - int userId = UserHandle.getUserId(uid); - verifyIncomingOp(code); - verifyIncomingUser(userId); - verifyIncomingPackage(packageName, userId); + verifyIncomingPackage(packageName, UserHandle.getUserId(uid)); ArraySet<ModeCallback> repCbs = null; code = AppOpsManager.opToSwitch(code); @@ -2729,9 +2713,6 @@ public class AppOpsService extends IAppOpsService.Stub { if (changed) { scheduleFastWriteLocked(); - - // Invalidate package info cache as the visibility of packages might have changed - PackageManager.invalidatePackageInfoCache(); } } if (callbacks != null) { @@ -2890,11 +2871,8 @@ public class AppOpsService extends IAppOpsService.Stub { private int checkOperationImpl(int code, int uid, String packageName, boolean raw) { - int userId = UserHandle.getUserId(uid); - verifyIncomingOp(code); - verifyIncomingUser(userId); - verifyIncomingPackage(packageName, userId); + verifyIncomingPackage(packageName, UserHandle.getUserId(uid)); String resolvedPackageName = resolvePackageName(uid, packageName); if (resolvedPackageName == null) { @@ -3013,15 +2991,10 @@ public class AppOpsService extends IAppOpsService.Stub { String proxiedAttributionTag, int proxyUid, String proxyPackageName, String proxyAttributionTag, boolean shouldCollectAsyncNotedOp, String message, boolean shouldCollectMessage) { - int proxiedUserId = UserHandle.getUserId(proxiedUid); - int proxyUserId = UserHandle.getUserId(proxyUid); - verifyIncomingUid(proxyUid); verifyIncomingOp(code); - verifyIncomingUser(proxiedUserId); - verifyIncomingUser(proxyUserId); - verifyIncomingPackage(proxiedPackageName, proxiedUserId); - verifyIncomingPackage(proxyPackageName, proxyUserId); + verifyIncomingPackage(proxiedPackageName, UserHandle.getUserId(proxiedUid)); + verifyIncomingPackage(proxyPackageName, UserHandle.getUserId(proxyUid)); String resolveProxyPackageName = resolvePackageName(proxyUid, proxyPackageName); if (resolveProxyPackageName == null) { @@ -3071,12 +3044,9 @@ public class AppOpsService extends IAppOpsService.Stub { private int noteOperationImpl(int code, int uid, @Nullable String packageName, @Nullable String attributionTag, boolean shouldCollectAsyncNotedOp, @Nullable String message, boolean shouldCollectMessage) { - int userId = UserHandle.getUserId(uid); - verifyIncomingUid(uid); verifyIncomingOp(code); - verifyIncomingUser(userId); - verifyIncomingPackage(packageName, userId); + verifyIncomingPackage(packageName, UserHandle.getUserId(uid)); String resolvedPackageName = resolvePackageName(uid, packageName); if (resolvedPackageName == null) { @@ -3453,12 +3423,9 @@ public class AppOpsService extends IAppOpsService.Stub { public int startOperation(IBinder clientId, int code, int uid, String packageName, String attributionTag, boolean startIfModeDefault, boolean shouldCollectAsyncNotedOp, String message, boolean shouldCollectMessage) { - int userId = UserHandle.getUserId(uid); - verifyIncomingUid(uid); verifyIncomingOp(code); - verifyIncomingUser(userId); - verifyIncomingPackage(packageName, userId); + verifyIncomingPackage(packageName, UserHandle.getUserId(uid)); String resolvedPackageName = resolvePackageName(uid, packageName); if (resolvedPackageName == null) { @@ -3550,12 +3517,9 @@ public class AppOpsService extends IAppOpsService.Stub { @Override public void finishOperation(IBinder clientId, int code, int uid, String packageName, String attributionTag) { - int userId = UserHandle.getUserId(uid); - verifyIncomingUid(uid); verifyIncomingOp(code); - verifyIncomingUser(userId); - verifyIncomingPackage(packageName, userId); + verifyIncomingPackage(packageName, UserHandle.getUserId(uid)); String resolvedPackageName = resolvePackageName(uid, packageName); if (resolvedPackageName == null) { @@ -3784,33 +3748,6 @@ public class AppOpsService extends IAppOpsService.Stub { } } - private void verifyIncomingUser(@UserIdInt int userId) { - int callingUid = Binder.getCallingUid(); - int callingUserId = UserHandle.getUserId(callingUid); - int callingPid = Binder.getCallingPid(); - - if (callingUserId != userId) { - // Prevent endless loop between when checking appops inside of handleIncomingUser - if (Binder.getCallingPid() == ActivityManagerService.MY_PID) { - return; - } - long token = Binder.clearCallingIdentity(); - try { - try { - LocalServices.getService(ActivityManagerInternal.class).handleIncomingUser( - callingPid, callingUid, userId, /* allowAll */ false, - ALLOW_ACROSS_PROFILES_IN_PROFILE_OR_NON_FULL, "appop operation", null); - } catch (Exception e) { - EventLog.writeEvent(0x534e4554, "153996875", "appop", userId); - - throw e; - } - } finally { - Binder.restoreCallingIdentity(token); - } - } - } - private @Nullable UidState getUidStateLocked(int uid, boolean edit) { UidState uidState = mUidStates.get(uid); if (uidState == null) { @@ -5890,11 +5827,8 @@ public class AppOpsService extends IAppOpsService.Stub { return false; } } - int userId = UserHandle.getUserId(uid); - verifyIncomingOp(code); - verifyIncomingUser(userId); - verifyIncomingPackage(packageName, userId); + verifyIncomingPackage(packageName, UserHandle.getUserId(uid)); final String resolvedPackageName = resolvePackageName(uid, packageName); if (resolvedPackageName == null) { diff --git a/services/core/java/com/android/server/appop/TEST_MAPPING b/services/core/java/com/android/server/appop/TEST_MAPPING index a3e1b7a7e5c5..84de25c06ebf 100644 --- a/services/core/java/com/android/server/appop/TEST_MAPPING +++ b/services/core/java/com/android/server/appop/TEST_MAPPING @@ -7,9 +7,6 @@ "name": "CtsAppOps2TestCases" }, { - "name": "CtsAppOpHostTestCases" - }, - { "name": "FrameworksServicesTests", "options": [ { diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index 6110759b5a9e..f1561cab8fb4 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -6735,14 +6735,17 @@ public class AudioService extends IAudioService.Stub if (msg.obj == null) { break; } - // If the app corresponding to this mode death handler object is not - // capturing or playing audio anymore after 3 seconds, remove it - // from the stack. Otherwise, check again in 3 seconds. + // If no other app is currently owning the audio mode and + // the app corresponding to this mode death handler object is still in the + // mode owner stack but not capturing or playing audio after 3 seconds, + // remove it from the stack. + // Otherwise, check again in 3 seconds. SetModeDeathHandler h = (SetModeDeathHandler) msg.obj; if (mSetModeDeathHandlers.indexOf(h) < 0) { break; } - if (mRecordMonitor.isRecordingActiveForUid(h.getUid()) + if (getModeOwnerUid() != h.getUid() + || mRecordMonitor.isRecordingActiveForUid(h.getUid()) || mPlaybackMonitor.isPlaybackActiveForUid(h.getUid())) { sendMsg(mAudioHandler, MSG_CHECK_MODE_FOR_UID, diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java index ded0f9a3dca7..8de31d999b53 100644 --- a/services/core/java/com/android/server/audio/BtHelper.java +++ b/services/core/java/com/android/server/audio/BtHelper.java @@ -450,6 +450,7 @@ public class BtHelper { } @GuardedBy("AudioDeviceBroker.mDeviceStateLock") + // @GuardedBy("BtHelper.this") private void stopAndRemoveClient(ScoClient client, @NonNull String eventSource) { AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(eventSource)); client.requestScoState(BluetoothHeadset.STATE_AUDIO_DISCONNECTED, diff --git a/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java b/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java index 0b4f31d365d3..28bd97e4843c 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java @@ -117,14 +117,15 @@ public class HdmiCecMessageValidator { // TODO: Validate more than length for the following messages. // Messages for the One Touch Record. - FixedLengthValidator oneByteValidator = new FixedLengthValidator(1); addValidationInfo(Constants.MESSAGE_RECORD_ON, new VariableLengthValidator(1, 8), DEST_DIRECT); - addValidationInfo(Constants.MESSAGE_RECORD_STATUS, oneByteValidator, DEST_DIRECT); + addValidationInfo(Constants.MESSAGE_RECORD_STATUS, + new RecordStatusInfoValidator(), DEST_DIRECT); // TODO: Handle messages for the Timer Programming. // Messages for the System Information. + FixedLengthValidator oneByteValidator = new FixedLengthValidator(1); addValidationInfo(Constants.MESSAGE_CEC_VERSION, oneByteValidator, DEST_DIRECT); addValidationInfo(Constants.MESSAGE_SET_MENU_LANGUAGE, new FixedLengthValidator(3), DEST_BROADCAST); @@ -339,4 +340,23 @@ public class HdmiCecMessageValidator { isValidPhysicalAddress(params, 0) && isValidPhysicalAddress(params, 2)); } } + + /** + * Check if the given record status message parameter is valid. + * A valid parameter should lie within the range description of Record Status Info defined in + * CEC 1.4 Specification : Operand Descriptions (Section 17) + */ + private class RecordStatusInfoValidator implements ParameterValidator { + @Override + public int isValid(byte[] params) { + if (params.length < 1) { + return ERROR_PARAMETER_SHORT; + } + return toErrorCode(isWithinRange(params[0], 0x01, 0x07) + || isWithinRange(params[0], 0x09, 0x0E) + || isWithinRange(params[0], 0x10, 0x17) + || isWithinRange(params[0], 0x1A, 0x1B) + || params[0] == 0x1F); + } + } } diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java index 68961d9c94d8..7e53e6f3e2c3 100644 --- a/services/core/java/com/android/server/locksettings/LockSettingsService.java +++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java @@ -2616,7 +2616,6 @@ public class LockSettingsService extends ILockSettings.Stub { final AuthenticationToken auth = mSpManager.newSyntheticPasswordAndSid( getGateKeeperService(), credentialHash, credential, userId); - onAuthTokenKnownForUser(userId, auth); if (auth == null) { Slog.wtf(TAG, "initializeSyntheticPasswordLocked returns null auth token"); return null; @@ -2639,6 +2638,7 @@ public class LockSettingsService extends ILockSettings.Stub { } fixateNewestUserKeyAuth(userId); setSyntheticPasswordHandleLocked(handle, userId); + onAuthTokenKnownForUser(userId, auth); return auth; } diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 7401403de127..1fd7a73f1174 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -309,6 +309,7 @@ import java.util.Set; import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; import java.util.function.BiConsumer; +import java.util.stream.Collectors; /** {@hide} */ public class NotificationManagerService extends SystemService { @@ -1528,10 +1529,10 @@ public class NotificationManagerService extends SystemService { cancelAllNotificationsInt(MY_UID, MY_PID, pkgName, null, 0, 0, !queryRestart, changeUserId, reason, null); } - } else if (hideNotifications) { - hideNotificationsForPackages(pkgList); - } else if (unhideNotifications) { - unhideNotificationsForPackages(pkgList); + } else if (hideNotifications && uidList != null && (uidList.length > 0)) { + hideNotificationsForPackages(pkgList, uidList); + } else if (unhideNotifications && uidList != null && (uidList.length > 0)) { + unhideNotificationsForPackages(pkgList, uidList); } } @@ -2076,6 +2077,68 @@ public class NotificationManagerService extends SystemService { mMsgPkgsAllowedAsConvos = Set.of(getStringArrayResource( com.android.internal.R.array.config_notificationMsgPkgsAllowedAsConvos)); mStatsManager = statsManager; + + // register for various Intents. + // If this is called within a test, make sure to unregister the intent receivers by + // calling onDestroy() + IntentFilter filter = new IntentFilter(); + filter.addAction(Intent.ACTION_SCREEN_ON); + filter.addAction(Intent.ACTION_SCREEN_OFF); + filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED); + filter.addAction(Intent.ACTION_USER_PRESENT); + filter.addAction(Intent.ACTION_USER_STOPPED); + filter.addAction(Intent.ACTION_USER_SWITCHED); + filter.addAction(Intent.ACTION_USER_ADDED); + filter.addAction(Intent.ACTION_USER_REMOVED); + filter.addAction(Intent.ACTION_USER_UNLOCKED); + filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE); + getContext().registerReceiverAsUser(mIntentReceiver, UserHandle.ALL, filter, null, null); + + IntentFilter pkgFilter = new IntentFilter(); + pkgFilter.addAction(Intent.ACTION_PACKAGE_ADDED); + pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED); + pkgFilter.addAction(Intent.ACTION_PACKAGE_CHANGED); + pkgFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED); + pkgFilter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART); + pkgFilter.addDataScheme("package"); + getContext().registerReceiverAsUser(mPackageIntentReceiver, UserHandle.ALL, pkgFilter, null, + null); + + IntentFilter suspendedPkgFilter = new IntentFilter(); + suspendedPkgFilter.addAction(Intent.ACTION_PACKAGES_SUSPENDED); + suspendedPkgFilter.addAction(Intent.ACTION_PACKAGES_UNSUSPENDED); + suspendedPkgFilter.addAction(Intent.ACTION_DISTRACTING_PACKAGES_CHANGED); + getContext().registerReceiverAsUser(mPackageIntentReceiver, UserHandle.ALL, + suspendedPkgFilter, null, null); + + IntentFilter sdFilter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE); + getContext().registerReceiverAsUser(mPackageIntentReceiver, UserHandle.ALL, sdFilter, null, + null); + + IntentFilter timeoutFilter = new IntentFilter(ACTION_NOTIFICATION_TIMEOUT); + timeoutFilter.addDataScheme(SCHEME_TIMEOUT); + getContext().registerReceiver(mNotificationTimeoutReceiver, timeoutFilter); + + IntentFilter settingsRestoredFilter = new IntentFilter(Intent.ACTION_SETTING_RESTORED); + getContext().registerReceiver(mRestoreReceiver, settingsRestoredFilter); + + IntentFilter localeChangedFilter = new IntentFilter(Intent.ACTION_LOCALE_CHANGED); + getContext().registerReceiver(mLocaleChangeReceiver, localeChangedFilter); + } + + /** + * Cleanup broadcast receivers change listeners. + */ + public void onDestroy() { + getContext().unregisterReceiver(mIntentReceiver); + getContext().unregisterReceiver(mPackageIntentReceiver); + getContext().unregisterReceiver(mNotificationTimeoutReceiver); + getContext().unregisterReceiver(mRestoreReceiver); + getContext().unregisterReceiver(mLocaleChangeReceiver); + + if (mDeviceConfigChangedListener != null) { + DeviceConfig.removeOnPropertiesChangedListener(mDeviceConfigChangedListener); + } } protected String[] getStringArrayResource(int key) { @@ -2126,51 +2189,6 @@ public class NotificationManagerService extends SystemService { Context.STATS_MANAGER), getContext().getSystemService(TelephonyManager.class)); - // register for various Intents - IntentFilter filter = new IntentFilter(); - filter.addAction(Intent.ACTION_SCREEN_ON); - filter.addAction(Intent.ACTION_SCREEN_OFF); - filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED); - filter.addAction(Intent.ACTION_USER_PRESENT); - filter.addAction(Intent.ACTION_USER_STOPPED); - filter.addAction(Intent.ACTION_USER_SWITCHED); - filter.addAction(Intent.ACTION_USER_ADDED); - filter.addAction(Intent.ACTION_USER_REMOVED); - filter.addAction(Intent.ACTION_USER_UNLOCKED); - filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE); - getContext().registerReceiverAsUser(mIntentReceiver, UserHandle.ALL, filter, null, null); - - IntentFilter pkgFilter = new IntentFilter(); - pkgFilter.addAction(Intent.ACTION_PACKAGE_ADDED); - pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED); - pkgFilter.addAction(Intent.ACTION_PACKAGE_CHANGED); - pkgFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED); - pkgFilter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART); - pkgFilter.addDataScheme("package"); - getContext().registerReceiverAsUser(mPackageIntentReceiver, UserHandle.ALL, pkgFilter, null, - null); - - IntentFilter suspendedPkgFilter = new IntentFilter(); - suspendedPkgFilter.addAction(Intent.ACTION_PACKAGES_SUSPENDED); - suspendedPkgFilter.addAction(Intent.ACTION_PACKAGES_UNSUSPENDED); - suspendedPkgFilter.addAction(Intent.ACTION_DISTRACTING_PACKAGES_CHANGED); - getContext().registerReceiverAsUser(mPackageIntentReceiver, UserHandle.ALL, - suspendedPkgFilter, null, null); - - IntentFilter sdFilter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE); - getContext().registerReceiverAsUser(mPackageIntentReceiver, UserHandle.ALL, sdFilter, null, - null); - - IntentFilter timeoutFilter = new IntentFilter(ACTION_NOTIFICATION_TIMEOUT); - timeoutFilter.addDataScheme(SCHEME_TIMEOUT); - getContext().registerReceiver(mNotificationTimeoutReceiver, timeoutFilter); - - IntentFilter settingsRestoredFilter = new IntentFilter(Intent.ACTION_SETTING_RESTORED); - getContext().registerReceiver(mRestoreReceiver, settingsRestoredFilter); - - IntentFilter localeChangedFilter = new IntentFilter(Intent.ACTION_LOCALE_CHANGED); - getContext().registerReceiver(mLocaleChangeReceiver, localeChangedFilter); - publishBinderService(Context.NOTIFICATION_SERVICE, mService, /* allowIsolated= */ false, DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PRIORITY_NORMAL); publishLocalService(NotificationManagerInternal.class, mInternalService); @@ -2193,12 +2211,6 @@ public class NotificationManagerService extends SystemService { mDeviceConfigChangedListener); } - void unregisterDeviceConfigChange() { - if (mDeviceConfigChangedListener != null) { - DeviceConfig.removeOnPropertiesChangedListener(mDeviceConfigChangedListener); - } - } - private void registerNotificationPreferencesPullers() { mPullAtomCallback = new StatsPullAtomCallbackImpl(); mStatsManager.setPullAtomCallback( @@ -8339,15 +8351,16 @@ public class NotificationManagerService extends SystemService { return -1; } - @VisibleForTesting - protected void hideNotificationsForPackages(String[] pkgs) { + private void hideNotificationsForPackages(@NonNull String[] pkgs, @NonNull int[] uidList) { synchronized (mNotificationLock) { + Set<Integer> uidSet = Arrays.stream(uidList).boxed().collect(Collectors.toSet()); List<String> pkgList = Arrays.asList(pkgs); List<NotificationRecord> changedNotifications = new ArrayList<>(); int numNotifications = mNotificationList.size(); for (int i = 0; i < numNotifications; i++) { NotificationRecord rec = mNotificationList.get(i); - if (pkgList.contains(rec.getSbn().getPackageName())) { + if (pkgList.contains(rec.getSbn().getPackageName()) + && uidSet.contains(rec.getUid())) { rec.setHidden(true); changedNotifications.add(rec); } @@ -8357,15 +8370,17 @@ public class NotificationManagerService extends SystemService { } } - @VisibleForTesting - protected void unhideNotificationsForPackages(String[] pkgs) { + private void unhideNotificationsForPackages(@NonNull String[] pkgs, + @NonNull int[] uidList) { synchronized (mNotificationLock) { + Set<Integer> uidSet = Arrays.stream(uidList).boxed().collect(Collectors.toSet()); List<String> pkgList = Arrays.asList(pkgs); List<NotificationRecord> changedNotifications = new ArrayList<>(); int numNotifications = mNotificationList.size(); for (int i = 0; i < numNotifications; i++) { NotificationRecord rec = mNotificationList.get(i); - if (pkgList.contains(rec.getSbn().getPackageName())) { + if (pkgList.contains(rec.getSbn().getPackageName()) + && uidSet.contains(rec.getUid())) { rec.setHidden(false); changedNotifications.add(rec); } @@ -9939,38 +9954,6 @@ public class NotificationManagerService extends SystemService { return CollectionUtils.firstOrNull(allowedComponents); } - @VisibleForTesting - protected void simulatePackageSuspendBroadcast(boolean suspend, String pkg) { - checkCallerIsSystemOrShell(); - // only use for testing: mimic receive broadcast that package is (un)suspended - // but does not actually (un)suspend the package - final Bundle extras = new Bundle(); - extras.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST, - new String[]{pkg}); - - final String action = suspend ? Intent.ACTION_PACKAGES_SUSPENDED - : Intent.ACTION_PACKAGES_UNSUSPENDED; - final Intent intent = new Intent(action); - intent.putExtras(extras); - - mPackageIntentReceiver.onReceive(getContext(), intent); - } - - @VisibleForTesting - protected void simulatePackageDistractionBroadcast(int flag, String[] pkgs) { - checkCallerIsSystemOrShell(); - // only use for testing: mimic receive broadcast that package is (un)distracting - // but does not actually register that info with packagemanager - final Bundle extras = new Bundle(); - extras.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST, pkgs); - extras.putInt(Intent.EXTRA_DISTRACTION_RESTRICTIONS, flag); - - final Intent intent = new Intent(Intent.ACTION_DISTRACTING_PACKAGES_CHANGED); - intent.putExtras(extras); - - mPackageIntentReceiver.onReceive(getContext(), intent); - } - /** * Wrapper for a StatusBarNotification object that allows transfer across a oneway * binder without sending large amounts of data over a oneway transaction. diff --git a/services/core/java/com/android/server/notification/NotificationShellCmd.java b/services/core/java/com/android/server/notification/NotificationShellCmd.java index da7864ba32c4..927dc25ac6ce 100644 --- a/services/core/java/com/android/server/notification/NotificationShellCmd.java +++ b/services/core/java/com/android/server/notification/NotificationShellCmd.java @@ -66,8 +66,6 @@ public class NotificationShellCmd extends ShellCommand { + " set_dnd [on|none (same as on)|priority|alarms|all|off (same as all)]" + " allow_dnd PACKAGE [user_id (current user if not specified)]\n" + " disallow_dnd PACKAGE [user_id (current user if not specified)]\n" - + " suspend_package PACKAGE\n" - + " unsuspend_package PACKAGE\n" + " reset_assistant_user_set [user_id (current user if not specified)]\n" + " get_approved_assistant [user_id (current user if not specified)]\n" + " post [--help | flags] TAG TEXT\n" @@ -258,25 +256,6 @@ public class NotificationShellCmd extends ShellCommand { mBinderService.setNotificationAssistantAccessGrantedForUser(cn, userId, false); } break; - case "suspend_package": { - // only use for testing - mDirectService.simulatePackageSuspendBroadcast(true, getNextArgRequired()); - } - break; - case "unsuspend_package": { - // only use for testing - mDirectService.simulatePackageSuspendBroadcast(false, getNextArgRequired()); - } - break; - case "distract_package": { - // only use for testing - // Flag values are in - // {@link android.content.pm.PackageManager.DistractionRestriction}. - mDirectService.simulatePackageDistractionBroadcast( - Integer.parseInt(getNextArgRequired()), - getNextArgRequired().split(",")); - break; - } case "reset_assistant_user_set": { int userId = ActivityManager.getCurrentUser(); if (peekNextArg() != null) { diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java index f52db5fb5f2f..31ee59717dba 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerService.java +++ b/services/core/java/com/android/server/pm/PackageInstallerService.java @@ -618,6 +618,13 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements } } + if ((params.installFlags & PackageManager.INSTALL_INSTANT_APP) != 0 + && !isCalledBySystemOrShell(callingUid) + && (mPm.getFlagsForUid(callingUid) & ApplicationInfo.FLAG_SYSTEM) == 0) { + throw new SecurityException( + "Only system apps could use the PackageManager.INSTALL_INSTANT_APP flag."); + } + if (params.isStaged && !isCalledBySystemOrShell(callingUid)) { if (mBypassNextStagedInstallerCheck) { mBypassNextStagedInstallerCheck = false; diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 697c31a78ec7..f94de227f3e9 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -1849,7 +1849,6 @@ public class PackageManagerService extends IPackageManager.Stub Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT); synchronized (mLock) { removeMessages(WRITE_PACKAGE_LIST); - mPermissionManager.writeStateToPackageSettingsTEMP(); mSettings.writePackageListLPr(msg.arg1); } Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); @@ -2686,7 +2685,8 @@ public class PackageManagerService extends IPackageManager.Stub (i, pm) -> new Settings(Environment.getDataDirectory(), i.getPermissionManagerServiceInternal().getPermissionSettings(), - RuntimePermissionsPersistence.createInstance(), lock), + RuntimePermissionsPersistence.createInstance(), + i.getPermissionManagerServiceInternal(), lock), new Injector.LocalServicesProducer<>(ActivityTaskManagerInternal.class), new Injector.LocalServicesProducer<>(ActivityManagerInternal.class), new Injector.LocalServicesProducer<>(DeviceIdleInternal.class), @@ -4486,8 +4486,8 @@ public class PackageManagerService extends IPackageManager.Stub AndroidPackage p = ps.pkg; if (p != null) { // Compute GIDs only if requested - final int[] gids = (flags & PackageManager.GET_GIDS) == 0 - ? EMPTY_INT_ARRAY : mPermissionManager.getPackageGids(ps.name, userId); + final int[] gids = (flags & PackageManager.GET_GIDS) == 0 ? EMPTY_INT_ARRAY + : mPermissionManager.getGidsForUid(UserHandle.getUid(userId, ps.appId)); // Compute granted permissions only if package has requested permissions final Set<String> permissions = ArrayUtils.isEmpty(p.getRequestedPermissions()) ? Collections.emptySet() @@ -4962,13 +4962,13 @@ public class PackageManagerService extends IPackageManager.Stub } // TODO: Shouldn't this be checking for package installed state for userId and // return null? - return mPermissionManager.getPackageGids(packageName, userId); + return mPermissionManager.getGidsForUid(UserHandle.getUid(userId, ps.appId)); } if ((flags & MATCH_KNOWN_PACKAGES) != 0) { final PackageSetting ps = mSettings.mPackages.get(packageName); if (ps != null && ps.isMatch(flags) && !shouldFilterApplicationLocked(ps, callingUid, userId)) { - return mPermissionManager.getPackageGids(packageName, userId); + return mPermissionManager.getGidsForUid(UserHandle.getUid(userId, ps.appId)); } } } @@ -18983,7 +18983,7 @@ public class PackageManagerService extends IPackageManager.Stub } if ((deletedPs.sharedUser == null || deletedPs.sharedUser.packages.size() == 0) && !isUpdatedSystemApp(deletedPs)) { - mPermissionManager.removePermissionsStateTEMP(removedAppId); + mPermissionManager.removeAppIdStateTEMP(removedAppId); } mPermissionManager.updatePermissions(deletedPs.name, null); if (deletedPs.sharedUser != null) { @@ -21242,7 +21242,8 @@ public class PackageManagerService extends IPackageManager.Stub // Prior to enabling the package, we need to decompress the APK(s) to the // data partition and then replace the version on the system partition. final AndroidPackage deletedPkg = pkgSetting.pkg; - final boolean isSystemStub = deletedPkg.isStub() + final boolean isSystemStub = (deletedPkg != null) + && deletedPkg.isStub() && deletedPkg.isSystem(); if (isSystemStub && (newState == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT @@ -21854,8 +21855,6 @@ public class PackageManagerService extends IPackageManager.Stub protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, TAG, pw)) return; - mPermissionManager.writeStateToPackageSettingsTEMP(); - DumpState dumpState = new DumpState(); boolean fullPreferred = false; boolean checkin = false; @@ -23734,7 +23733,6 @@ public class PackageManagerService extends IPackageManager.Stub mDirtyUsers.remove(userId); mUserNeedsBadging.delete(userId); mPermissionManager.onUserRemoved(userId); - mPermissionManager.writeStateToPackageSettingsTEMP(); mSettings.removeUserLPw(userId); mPendingBroadcasts.remove(userId); mInstantAppRegistry.onUserRemovedLPw(userId); diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java index d77683e8ba61..afbf7d3a35af 100644 --- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java +++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java @@ -2323,7 +2323,8 @@ class PackageManagerShellCommand extends ShellCommand { private boolean isVendorApp(String pkg) { try { - final PackageInfo info = mInterface.getPackageInfo(pkg, 0, UserHandle.USER_SYSTEM); + final PackageInfo info = mInterface.getPackageInfo( + pkg, PackageManager.MATCH_ANY_USER, UserHandle.USER_SYSTEM); return info != null && info.applicationInfo.isVendor(); } catch (RemoteException e) { return false; @@ -2332,7 +2333,8 @@ class PackageManagerShellCommand extends ShellCommand { private boolean isProductApp(String pkg) { try { - final PackageInfo info = mInterface.getPackageInfo(pkg, 0, UserHandle.USER_SYSTEM); + final PackageInfo info = mInterface.getPackageInfo( + pkg, PackageManager.MATCH_ANY_USER, UserHandle.USER_SYSTEM); return info != null && info.applicationInfo.isProduct(); } catch (RemoteException e) { return false; @@ -2341,7 +2343,8 @@ class PackageManagerShellCommand extends ShellCommand { private boolean isSystemExtApp(String pkg) { try { - final PackageInfo info = mInterface.getPackageInfo(pkg, 0, UserHandle.USER_SYSTEM); + final PackageInfo info = mInterface.getPackageInfo( + pkg, PackageManager.MATCH_ANY_USER, UserHandle.USER_SYSTEM); return info != null && info.applicationInfo.isSystemExt(); } catch (RemoteException e) { return false; diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java index 276f88082df0..855a5ff524fd 100644 --- a/services/core/java/com/android/server/pm/PackageSetting.java +++ b/services/core/java/com/android/server/pm/PackageSetting.java @@ -28,7 +28,7 @@ import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.VisibleForTesting; import com.android.server.pm.parsing.pkg.AndroidPackage; -import com.android.server.pm.permission.PermissionsState; +import com.android.server.pm.permission.AppIdPermissionState; import com.android.server.pm.pkg.PackageStateUnserialized; import java.io.File; @@ -215,7 +215,7 @@ public class PackageSetting extends PackageSettingBase { } @Override - public PermissionsState getPermissionsState() { + public AppIdPermissionState getPermissionsState() { return (sharedUser != null) ? sharedUser.getPermissionsState() : super.getPermissionsState(); diff --git a/services/core/java/com/android/server/pm/SettingBase.java b/services/core/java/com/android/server/pm/SettingBase.java index 3e2ab05e83ec..c1258b1efd48 100644 --- a/services/core/java/com/android/server/pm/SettingBase.java +++ b/services/core/java/com/android/server/pm/SettingBase.java @@ -19,23 +19,23 @@ package com.android.server.pm; import android.content.pm.ApplicationInfo; import com.android.internal.annotations.VisibleForTesting; -import com.android.server.pm.permission.PermissionsState; +import com.android.server.pm.permission.AppIdPermissionState; @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) public abstract class SettingBase { int pkgFlags; int pkgPrivateFlags; - protected final PermissionsState mPermissionsState; + protected final AppIdPermissionState mPermissionsState; SettingBase(int pkgFlags, int pkgPrivateFlags) { setFlags(pkgFlags); setPrivateFlags(pkgPrivateFlags); - mPermissionsState = new PermissionsState(); + mPermissionsState = new AppIdPermissionState(); } SettingBase(SettingBase orig) { - mPermissionsState = new PermissionsState(); + mPermissionsState = new AppIdPermissionState(); doCopy(orig); } @@ -49,7 +49,7 @@ public abstract class SettingBase { mPermissionsState.copyFrom(orig.mPermissionsState); } - public PermissionsState getPermissionsState() { + public AppIdPermissionState getPermissionsState() { return mPermissionsState; } diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index 965430727d3c..bae36b2ad353 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -79,6 +79,7 @@ import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; import android.util.AtomicFile; +import android.util.IntArray; import android.util.Log; import android.util.LogPrinter; import android.util.Pair; @@ -106,10 +107,11 @@ import com.android.server.pm.Installer.InstallerException; import com.android.server.pm.parsing.PackageInfoUtils; import com.android.server.pm.parsing.pkg.AndroidPackage; import com.android.server.pm.parsing.pkg.AndroidPackageUtils; +import com.android.server.pm.permission.AppIdPermissionState; +import com.android.server.pm.permission.AppIdPermissionState.PermissionState; import com.android.server.pm.permission.BasePermission; +import com.android.server.pm.permission.LegacyPermissionDataProvider; import com.android.server.pm.permission.PermissionSettings; -import com.android.server.pm.permission.PermissionsState; -import com.android.server.pm.permission.PermissionsState.PermissionState; import com.android.server.utils.TimingsTraceAndSlog; import libcore.io.IoUtils; @@ -416,9 +418,12 @@ public final class Settings { private final File mSystemDir; public final KeySetManagerService mKeySetManagerService = new KeySetManagerService(mPackages); + /** Settings and other information about permissions */ final PermissionSettings mPermissions; + private final LegacyPermissionDataProvider mPermissionDataProvider; + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) public Settings(Map<String, PackageSetting> pkgSettings) { mLock = new Object(); @@ -426,6 +431,7 @@ public final class Settings { mSystemDir = null; mPermissions = null; mRuntimePermissionsPersistence = null; + mPermissionDataProvider = null; mSettingsFilename = null; mBackupSettingsFilename = null; mPackageListFilename = null; @@ -434,12 +440,14 @@ public final class Settings { mKernelMappingFilename = null; } - Settings(File dataDir, PermissionSettings permission, - RuntimePermissionsPersistence runtimePermissionsPersistence, Object lock) { + Settings(File dataDir, PermissionSettings permissionSettings, + RuntimePermissionsPersistence runtimePermissionsPersistence, + LegacyPermissionDataProvider permissionDataProvider, Object lock) { mLock = lock; - mPermissions = permission; + mPermissions = permissionSettings; mRuntimePermissionsPersistence = new RuntimePermissionPersistence( - runtimePermissionsPersistence, mLock); + runtimePermissionsPersistence); + mPermissionDataProvider = permissionDataProvider; mSystemDir = new File(dataDir, "system"); mSystemDir.mkdirs(); @@ -1239,7 +1247,7 @@ public final class Settings { void writeAllRuntimePermissionsLPr() { for (int userId : UserManagerService.getInstance().getUserIds()) { - mRuntimePermissionsPersistence.writePermissionsForUserAsyncLPr(userId); + mRuntimePermissionsPersistence.writeStateForUserAsyncLPr(userId); } } @@ -2102,7 +2110,7 @@ public final class Settings { } void readInstallPermissionsLPr(XmlPullParser parser, - PermissionsState permissionsState) throws IOException, XmlPullParserException { + AppIdPermissionState permissionsState) throws IOException, XmlPullParserException { int outerDepth = parser.getDepth(); int type; while ((type=parser.next()) != XmlPullParser.END_DOCUMENT @@ -2131,25 +2139,7 @@ public final class Settings { final int flags = (flagsStr != null) ? Integer.parseInt(flagsStr, 16) : 0; - if (granted) { - if (permissionsState.grantInstallPermission(bp) == - PermissionsState.PERMISSION_OPERATION_FAILURE) { - Slog.w(PackageManagerService.TAG, "Permission already added: " + name); - XmlUtils.skipCurrentTag(parser); - } else { - permissionsState.updatePermissionFlags(bp, UserHandle.USER_ALL, - PackageManager.MASK_PERMISSION_FLAGS_ALL, flags); - } - } else { - if (permissionsState.revokeInstallPermission(bp) == - PermissionsState.PERMISSION_OPERATION_FAILURE) { - Slog.w(PackageManagerService.TAG, "Permission already added: " + name); - XmlUtils.skipCurrentTag(parser); - } else { - permissionsState.updatePermissionFlags(bp, UserHandle.USER_ALL, - PackageManager.MASK_PERMISSION_FLAGS_ALL, flags); - } - } + permissionsState.putInstallPermissionState(new PermissionState(bp, granted, flags)); } else { Slog.w(PackageManagerService.TAG, "Unknown element under <permissions>: " + parser.getName()); @@ -2158,7 +2148,7 @@ public final class Settings { } } - void writePermissionsLPr(XmlSerializer serializer, List<PermissionState> permissionStates) + void writePermissionsLPr(XmlSerializer serializer, Collection<PermissionState> permissionStates) throws IOException { if (permissionStates.isEmpty()) { return; @@ -2641,7 +2631,11 @@ public final class Settings { } final boolean isDebug = pkg.pkg.isDebuggable(); - final int[] gids = pkg.getPermissionsState().computeGids(userIds); + final IntArray gids = new IntArray(); + for (final int userId : userIds) { + gids.addAll(mPermissionDataProvider.getGidsForUid(UserHandle.getUid(userId, + pkg.appId))); + } // Avoid any application that has a space in its path. if (dataPath.indexOf(' ') >= 0) @@ -2673,11 +2667,12 @@ public final class Settings { sb.append(" "); sb.append(AndroidPackageUtils.getSeInfo(pkg.pkg, pkg)); sb.append(" "); - if (gids != null && gids.length > 0) { - sb.append(gids[0]); - for (int i = 1; i < gids.length; i++) { + final int gidsSize = gids.size(); + if (gids != null && gids.size() > 0) { + sb.append(gids.get(0)); + for (int i = 1; i < gidsSize; i++) { sb.append(","); - sb.append(gids[i]); + sb.append(gids.get(i)); } } else { sb.append("none"); @@ -4482,8 +4477,9 @@ public final class Settings { } void dumpPackageLPr(PrintWriter pw, String prefix, String checkinTag, - ArraySet<String> permissionNames, PackageSetting ps, SimpleDateFormat sdf, - Date date, List<UserInfo> users, boolean dumpAll, boolean dumpAllComponents) { + ArraySet<String> permissionNames, PackageSetting ps, + AppIdPermissionState permissionsState, SimpleDateFormat sdf, Date date, + List<UserInfo> users, boolean dumpAll, boolean dumpAllComponents) { AndroidPackage pkg = ps.pkg; if (checkinTag != null) { pw.print(checkinTag); @@ -4810,7 +4806,6 @@ public final class Settings { } if (ps.sharedUser == null || permissionNames != null || dumpAll) { - PermissionsState permissionsState = ps.getPermissionsState(); dumpInstallPermissionsLPr(pw, prefix + " ", permissionNames, permissionsState); } @@ -4889,8 +4884,8 @@ public final class Settings { } if (ps.sharedUser == null) { - PermissionsState permissionsState = ps.getPermissionsState(); - dumpGidsLPr(pw, prefix + " ", permissionsState.computeGids(user.id)); + dumpGidsLPr(pw, prefix + " ", mPermissionDataProvider.getGidsForUid( + UserHandle.getUid(user.id, ps.appId))); dumpRuntimePermissionsLPr(pw, prefix + " ", permissionNames, permissionsState .getRuntimePermissionStates(user.id), dumpAll); } @@ -4933,8 +4928,10 @@ public final class Settings { && !packageName.equals(ps.name)) { continue; } + final AppIdPermissionState permissionsState = + mPermissionDataProvider.getAppIdPermissionState(ps.appId); if (permissionNames != null - && !ps.getPermissionsState().hasRequestedPermission(permissionNames)) { + && !permissionsState.hasPermissionState(permissionNames)) { continue; } @@ -4948,8 +4945,8 @@ public final class Settings { pw.println("Packages:"); printedSomething = true; } - dumpPackageLPr(pw, " ", checkin ? "pkg" : null, permissionNames, ps, sdf, date, users, - packageName != null, dumpAllComponents); + dumpPackageLPr(pw, " ", checkin ? "pkg" : null, permissionNames, ps, permissionsState, + sdf, date, users, packageName != null, dumpAllComponents); } printedSomething = false; @@ -4989,8 +4986,10 @@ public final class Settings { pw.println("Hidden system packages:"); printedSomething = true; } - dumpPackageLPr(pw, " ", checkin ? "dis" : null, permissionNames, ps, sdf, date, - users, packageName != null, dumpAllComponents); + final AppIdPermissionState permissionsState = + mPermissionDataProvider.getAppIdPermissionState(ps.appId); + dumpPackageLPr(pw, " ", checkin ? "dis" : null, permissionNames, ps, + permissionsState, sdf, date, users, packageName != null, dumpAllComponents); } } } @@ -5018,8 +5017,10 @@ public final class Settings { if (packageName != null && su != dumpState.getSharedUser()) { continue; } + final AppIdPermissionState permissionsState = + mPermissionDataProvider.getAppIdPermissionState(su.userId); if (permissionNames != null - && !su.getPermissionsState().hasRequestedPermission(permissionNames)) { + && !permissionsState.hasPermissionState(permissionNames)) { continue; } if (!checkin) { @@ -5054,12 +5055,12 @@ public final class Settings { continue; } - final PermissionsState permissionsState = su.getPermissionsState(); dumpInstallPermissionsLPr(pw, prefix, permissionNames, permissionsState); for (int userId : UserManagerService.getInstance().getUserIds()) { - final int[] gids = permissionsState.computeGids(userId); - final List<PermissionState> permissions = + final int[] gids = mPermissionDataProvider.getGidsForUid(UserHandle.getUid( + userId, su.userId)); + final Collection<PermissionState> permissions = permissionsState.getRuntimePermissionStates(userId); if (!ArrayUtils.isEmpty(gids) || !permissions.isEmpty()) { pw.print(prefix); pw.print("User "); pw.print(userId); pw.println(": "); @@ -5120,7 +5121,7 @@ public final class Settings { } void dumpRuntimePermissionsLPr(PrintWriter pw, String prefix, ArraySet<String> permissionNames, - List<PermissionState> permissionStates, boolean dumpAll) { + Collection<PermissionState> permissionStates, boolean dumpAll) { if (!permissionStates.isEmpty() || dumpAll) { pw.print(prefix); pw.println("runtime permissions:"); for (PermissionState permissionState : permissionStates) { @@ -5161,8 +5162,9 @@ public final class Settings { } void dumpInstallPermissionsLPr(PrintWriter pw, String prefix, ArraySet<String> permissionNames, - PermissionsState permissionsState) { - List<PermissionState> permissionStates = permissionsState.getInstallPermissionStates(); + AppIdPermissionState permissionsState) { + Collection<PermissionState> permissionStates = + permissionsState.getInstallPermissionStates(); if (!permissionStates.isEmpty()) { pw.print(prefix); pw.println("install permissions:"); for (PermissionState permissionState : permissionStates) { @@ -5202,9 +5204,9 @@ public final class Settings { public void writeRuntimePermissionsForUserLPr(int userId, boolean sync) { if (sync) { - mRuntimePermissionsPersistence.writePermissionsForUserSyncLPr(userId); + mRuntimePermissionsPersistence.writeStateForUserSyncLPr(userId); } else { - mRuntimePermissionsPersistence.writePermissionsForUserAsyncLPr(userId); + mRuntimePermissionsPersistence.writeStateForUserAsyncLPr(userId); } } @@ -5292,8 +5294,6 @@ public final class Settings { private final Handler mHandler = new MyHandler(); - private final Object mPersistenceLock; - @GuardedBy("mLock") private final SparseBooleanArray mWriteScheduled = new SparseBooleanArray(); @@ -5313,10 +5313,8 @@ public final class Settings { // The mapping keys are user ids. private final SparseBooleanArray mPermissionUpgradeNeeded = new SparseBooleanArray(); - public RuntimePermissionPersistence(RuntimePermissionsPersistence persistence, - Object persistenceLock) { + public RuntimePermissionPersistence(RuntimePermissionsPersistence persistence) { mPersistence = persistence; - mPersistenceLock = persistenceLock; } @GuardedBy("Settings.this.mLock") @@ -5327,7 +5325,7 @@ public final class Settings { @GuardedBy("Settings.this.mLock") void setVersionLPr(int version, int userId) { mVersions.put(userId, version); - writePermissionsForUserAsyncLPr(userId); + writeStateForUserAsyncLPr(userId); } @GuardedBy("Settings.this.mLock") @@ -5342,7 +5340,7 @@ public final class Settings { + "set before trying to update the fingerprint."); } mFingerprints.put(userId, mExtendedFingerprint); - writePermissionsForUserAsyncLPr(userId); + writeStateForUserAsyncLPr(userId); } public void setPermissionControllerVersion(long version) { @@ -5361,13 +5359,7 @@ public final class Settings { return Build.FINGERPRINT + "?pc_version=" + version; } - public void writePermissionsForUserSyncLPr(int userId) { - mHandler.removeMessages(userId); - writePermissionsSync(userId); - } - - @GuardedBy("Settings.this.mLock") - public void writePermissionsForUserAsyncLPr(int userId) { + public void writeStateForUserAsyncLPr(int userId) { final long currentTimeMillis = SystemClock.uptimeMillis(); if (mWriteScheduled.get(userId)) { @@ -5399,59 +5391,53 @@ public final class Settings { } } - private void writePermissionsSync(int userId) { - RuntimePermissionsState runtimePermissions; - synchronized (mPersistenceLock) { - mWriteScheduled.delete(userId); - - int version = mVersions.get(userId, INITIAL_VERSION); + public void writeStateForUserSyncLPr(int userId) { + mHandler.removeMessages(userId); + mWriteScheduled.delete(userId); - String fingerprint = mFingerprints.get(userId); + int version = mVersions.get(userId, INITIAL_VERSION); - Map<String, List<RuntimePermissionsState.PermissionState>> packagePermissions = - new ArrayMap<>(); - int packagesSize = mPackages.size(); - for (int i = 0; i < packagesSize; i++) { - String packageName = mPackages.keyAt(i); - PackageSetting packageSetting = mPackages.valueAt(i); - if (packageSetting.sharedUser == null) { - List<RuntimePermissionsState.PermissionState> permissions = - getPermissionsFromPermissionsState( - packageSetting.getPermissionsState(), userId); - packagePermissions.put(packageName, permissions); - } - } + String fingerprint = mFingerprints.get(userId); - Map<String, List<RuntimePermissionsState.PermissionState>> sharedUserPermissions = - new ArrayMap<>(); - final int sharedUsersSize = mSharedUsers.size(); - for (int i = 0; i < sharedUsersSize; i++) { - String sharedUserName = mSharedUsers.keyAt(i); - SharedUserSetting sharedUserSetting = mSharedUsers.valueAt(i); + Map<String, List<RuntimePermissionsState.PermissionState>> packagePermissions = + new ArrayMap<>(); + int packagesSize = mPackages.size(); + for (int i = 0; i < packagesSize; i++) { + String packageName = mPackages.keyAt(i); + PackageSetting packageSetting = mPackages.valueAt(i); + if (packageSetting.sharedUser == null) { List<RuntimePermissionsState.PermissionState> permissions = getPermissionsFromPermissionsState( - sharedUserSetting.getPermissionsState(), userId); - sharedUserPermissions.put(sharedUserName, permissions); + packageSetting.getPermissionsState(), userId); + packagePermissions.put(packageName, permissions); } + } - runtimePermissions = new RuntimePermissionsState(version, fingerprint, - packagePermissions, sharedUserPermissions); + Map<String, List<RuntimePermissionsState.PermissionState>> sharedUserPermissions = + new ArrayMap<>(); + final int sharedUsersSize = mSharedUsers.size(); + for (int i = 0; i < sharedUsersSize; i++) { + String sharedUserName = mSharedUsers.keyAt(i); + SharedUserSetting sharedUserSetting = mSharedUsers.valueAt(i); + List<RuntimePermissionsState.PermissionState> permissions = + getPermissionsFromPermissionsState( + sharedUserSetting.getPermissionsState(), userId); + sharedUserPermissions.put(sharedUserName, permissions); } + RuntimePermissionsState runtimePermissions = new RuntimePermissionsState(version, + fingerprint, packagePermissions, sharedUserPermissions); + mPersistence.writeForUser(runtimePermissions, UserHandle.of(userId)); } @NonNull private List<RuntimePermissionsState.PermissionState> getPermissionsFromPermissionsState( - @NonNull PermissionsState permissionsState, @UserIdInt int userId) { - List<PermissionState> permissionStates = permissionsState.getRuntimePermissionStates( - userId); - List<RuntimePermissionsState.PermissionState> permissions = - new ArrayList<>(); - int permissionStatesSize = permissionStates.size(); - for (int i = 0; i < permissionStatesSize; i++) { - PermissionState permissionState = permissionStates.get(i); - + @NonNull AppIdPermissionState permissionsState, @UserIdInt int userId) { + Collection<PermissionState> permissionStates = + permissionsState.getRuntimePermissionStates(userId); + List<RuntimePermissionsState.PermissionState> permissions = new ArrayList<>(); + for (PermissionState permissionState : permissionStates) { RuntimePermissionsState.PermissionState permission = new RuntimePermissionsState.PermissionState(permissionState.getName(), permissionState.isGranted(), permissionState.getFlags()); @@ -5480,7 +5466,7 @@ public final class Settings { userId)); if (runtimePermissions == null) { readLegacyStateForUserSyncLPr(userId); - writePermissionsForUserAsyncLPr(userId); + writeStateForUserAsyncLPr(userId); return; } @@ -5536,7 +5522,7 @@ public final class Settings { private void readPermissionsStateLpr( @NonNull List<RuntimePermissionsState.PermissionState> permissions, - @NonNull PermissionsState permissionsState, @UserIdInt int userId) { + @NonNull AppIdPermissionState permissionsState, @UserIdInt int userId) { int permissionsSize = permissions.size(); for (int i = 0; i < permissionsSize; i++) { RuntimePermissionsState.PermissionState permission = permissions.get(i); @@ -5550,14 +5536,8 @@ public final class Settings { boolean granted = permission.isGranted(); int flags = permission.getFlags(); - if (granted) { - permissionsState.grantRuntimePermission(basePermission, userId); - permissionsState.updatePermissionFlags(basePermission, userId, - PackageManager.MASK_PERMISSION_FLAGS_ALL, flags); - } else { - permissionsState.updatePermissionFlags(basePermission, userId, - PackageManager.MASK_PERMISSION_FLAGS_ALL, flags); - } + permissionsState.putRuntimePermissionState(new PermissionState(basePermission, + granted, flags), userId); } } @@ -5638,8 +5618,9 @@ public final class Settings { } } - private void parsePermissionsLPr(XmlPullParser parser, PermissionsState permissionsState, - int userId) throws IOException, XmlPullParserException { + private void parsePermissionsLPr(XmlPullParser parser, + AppIdPermissionState permissionsState, int userId) + throws IOException, XmlPullParserException { final int outerDepth = parser.getDepth(); int type; while ((type = parser.next()) != XmlPullParser.END_DOCUMENT @@ -5666,15 +5647,8 @@ public final class Settings { final int flags = (flagsStr != null) ? Integer.parseInt(flagsStr, 16) : 0; - if (granted) { - permissionsState.grantRuntimePermission(bp, userId); - permissionsState.updatePermissionFlags(bp, userId, - PackageManager.MASK_PERMISSION_FLAGS_ALL, flags); - } else { - permissionsState.updatePermissionFlags(bp, userId, - PackageManager.MASK_PERMISSION_FLAGS_ALL, flags); - } - + permissionsState.putRuntimePermissionState(new PermissionState(bp, granted, + flags), userId); } break; } @@ -5690,7 +5664,9 @@ public final class Settings { public void handleMessage(Message message) { final int userId = message.what; Runnable callback = (Runnable) message.obj; - writePermissionsSync(userId); + synchronized (mLock) { + writeStateForUserSyncLPr(userId); + } if (callback != null) { callback.run(); } diff --git a/services/core/java/com/android/server/pm/permission/AppIdPermissionState.java b/services/core/java/com/android/server/pm/permission/AppIdPermissionState.java new file mode 100644 index 000000000000..aabdafdd453a --- /dev/null +++ b/services/core/java/com/android/server/pm/permission/AppIdPermissionState.java @@ -0,0 +1,320 @@ +/* + * Copyright (C) 2015 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.permission; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.UserIdInt; +import android.os.UserHandle; +import android.util.ArrayMap; +import android.util.SparseArray; +import android.util.SparseBooleanArray; + +import java.util.Collection; +import java.util.Collections; +import java.util.Objects; + +/** + * Legacy permission state that was associated with packages or shared users. + */ +//@SystemApi(client = SystemApi.Client.SYSTEM_SERVER) +public final class AppIdPermissionState { + // Maps from user IDs to user states. + @NonNull + private final SparseArray<UserState> mUserStates = new SparseArray<>(); + + // Keyed by user IDs. + @NonNull + private final SparseBooleanArray mMissing = new SparseBooleanArray(); + + /** + * Copy from another permission state. + * + * @param other the other permission state. + * + * @hide + */ + public void copyFrom(@NonNull AppIdPermissionState other) { + if (other == this) { + return; + } + + mUserStates.clear(); + final int userStatesSize = other.mUserStates.size(); + for (int i = 0; i < userStatesSize; i++) { + mUserStates.put(other.mUserStates.keyAt(i), + new UserState(other.mUserStates.valueAt(i))); + } + + mMissing.clear(); + final int missingSize = other.mMissing.size(); + for (int i = 0; i < missingSize; i++) { + mMissing.put(other.mMissing.keyAt(i), other.mMissing.valueAt(i)); + } + } + + /** + * Reset this permission state. + * + * @hide + */ + public void reset() { + mUserStates.clear(); + mMissing.clear(); + } + + @Override + public boolean equals(@Nullable Object object) { + if (this == object) { + return true; + } + if (object == null) { + return false; + } + if (getClass() != object.getClass()) { + return false; + } + final AppIdPermissionState other = (AppIdPermissionState) object; + return Objects.equals(mUserStates, other.mUserStates) + && Objects.equals(mMissing, other.mMissing); + } + + /** + * Put a install permission state. + * + * @param permissionState the permission state + */ + public void putInstallPermissionState(@NonNull PermissionState permissionState) { + putPermissionState(permissionState, UserHandle.USER_ALL); + } + + /** + * Put a runtime permission state for a user. + * + * @param permissionState the permission state + * @param userId the user ID + */ + public void putRuntimePermissionState(@NonNull PermissionState permissionState, + @UserIdInt int userId) { + checkUserId(userId); + putPermissionState(permissionState, userId); + } + + private void putPermissionState(@NonNull PermissionState permissionState, + @UserIdInt int userId) { + UserState userState = mUserStates.get(userId); + if (userState == null) { + userState = new UserState(); + mUserStates.put(userId, userState); + } + userState.putPermissionState(permissionState); + } + + /** + * Check whether there are any permission states for the given permissions. + * + * @param permissionNames the permission names + * @return whether there are any permission states + * + * @hide + */ + public boolean hasPermissionState(@NonNull Collection<String> permissionNames) { + final int userStatesSize = mUserStates.size(); + for (int i = 0; i < userStatesSize; i++) { + final UserState userState = mUserStates.valueAt(i); + for (final String permissionName : permissionNames) { + if (userState.getPermissionState(permissionName) != null) { + return true; + } + } + } + return false; + } + + /** + * Get all the install permission states. + * + * @return the install permission states + */ + @NonNull + public Collection<PermissionState> getInstallPermissionStates() { + return getPermissionStates(UserHandle.USER_ALL); + } + + /** + * Get all the runtime permission states for a user. + * + * @param userId the user ID + * @return the runtime permission states + */ + @NonNull + public Collection<PermissionState> getRuntimePermissionStates(@UserIdInt int userId) { + checkUserId(userId); + return getPermissionStates(userId); + } + + @NonNull + private Collection<PermissionState> getPermissionStates(@UserIdInt int userId) { + final UserState userState = mUserStates.get(userId); + if (userState == null) { + return Collections.emptyList(); + } + return userState.getPermissionStates(); + } + + /** + * Check whether the permission state is missing for a user. + * <p> + * This can happen if permission state is rolled back and we'll need to generate a reasonable + * default state to keep the app usable. + * + * @param userId the user ID + * @return whether the permission state is missing + */ + public boolean isMissing(@UserIdInt int userId) { + checkUserId(userId); + return mMissing.get(userId); + } + + /** + * Set whether the permission state is missing for a user. + * <p> + * This can happen if permission state is rolled back and we'll need to generate a reasonable + * default state to keep the app usable. + * + * @param missing whether the permission state is missing + * @param userId the user ID + */ + public void setMissing(boolean missing, @UserIdInt int userId) { + checkUserId(userId); + if (missing) { + mMissing.put(userId, true); + } else { + mMissing.delete(userId); + } + } + + private static void checkUserId(@UserIdInt int userId) { + if (userId < 0) { + throw new IllegalArgumentException("Invalid user ID " + userId); + } + } + + /** + * Legacy state for permissions for a user. + */ + private static final class UserState { + // Maps from permission names to permission states. + @NonNull + private final ArrayMap<String, PermissionState> mPermissionStates = new ArrayMap<>(); + + public UserState() {} + + public UserState(@NonNull UserState other) { + final int permissionStatesSize = other.mPermissionStates.size(); + for (int i = 0; i < permissionStatesSize; i++) { + mPermissionStates.put(other.mPermissionStates.keyAt(i), + new PermissionState(other.mPermissionStates.valueAt(i))); + } + } + + @Nullable + public PermissionState getPermissionState(@NonNull String permissionName) { + return mPermissionStates.get(permissionName); + } + + public void putPermissionState(@NonNull PermissionState permissionState) { + mPermissionStates.put(permissionState.getName(), permissionState); + } + + @NonNull + public Collection<PermissionState> getPermissionStates() { + return Collections.unmodifiableCollection(mPermissionStates.values()); + } + } + + /** + * Legacy state for a single permission. + */ + public static final class PermissionState { + @NonNull + private final BasePermission mPermission; + + private final boolean mGranted; + + private final int mFlags; + + /** + * Create a new instance of this class. + * + * @param permission the {@link BasePermission} for the permission + * @param granted whether the permission is granted + * @param flags the permission flags + */ + public PermissionState(@NonNull BasePermission permission, boolean granted, int flags) { + mPermission = permission; + mGranted = granted; + mFlags = flags; + } + + private PermissionState(@NonNull PermissionState other) { + mPermission = other.mPermission; + mGranted = other.mGranted; + mFlags = other.mFlags; + } + + /** + * Get the {@link BasePermission} for the permission. + * + * @return the {@link BasePermission} + */ + @NonNull + public BasePermission getPermission() { + return mPermission; + } + + /** + * Get the permission name. + * + * @return the permission name + */ + @NonNull + public String getName() { + return mPermission.getName(); + } + + /** + * Get whether the permission is granted. + * + * @return whether the permission is granted + */ + @NonNull + public boolean isGranted() { + return mGranted; + } + + /** + * Get the permission flags. + * + * @return the permission flags + */ + @NonNull + public int getFlags() { + return mFlags; + } + } +} diff --git a/services/core/java/com/android/server/pm/permission/LegacyPermissionDataProvider.java b/services/core/java/com/android/server/pm/permission/LegacyPermissionDataProvider.java new file mode 100644 index 000000000000..7452b522b20a --- /dev/null +++ b/services/core/java/com/android/server/pm/permission/LegacyPermissionDataProvider.java @@ -0,0 +1,44 @@ +/* + * 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.server.pm.permission; + +import android.annotation.AppIdInt; +import android.annotation.NonNull; + +/** + * An interface for legacy code to read permission data in order to maintain compatibility. + */ +//@SystemApi(client = SystemApi.Client.SYSTEM_SERVER) +public interface LegacyPermissionDataProvider { + /** + * Get the legacy permission state of an app ID, either a package or a shared user. + * + * @param appId the app ID + * @return the legacy permission state + */ + @NonNull + public abstract AppIdPermissionState getAppIdPermissionState(@AppIdInt int appId); + + /** + * Get the GIDs computed from the permission state of a UID, either a package or a shared user. + * + * @param uid the UID + * @return the GIDs for the UID + */ + @NonNull + public abstract int[] getGidsForUid(int uid); +} 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 aa327ba02356..544f1225916e 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java @@ -58,6 +58,7 @@ import static com.android.server.pm.permission.UidPermissionState.PERMISSION_OPE import static java.util.concurrent.TimeUnit.SECONDS; import android.Manifest; +import android.annotation.AppIdInt; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; @@ -137,7 +138,6 @@ import com.android.server.LocalServices; import com.android.server.ServiceThread; import com.android.server.SystemConfig; import com.android.server.Watchdog; -import com.android.server.am.ActivityManagerService; import com.android.server.pm.ApexManager; import com.android.server.pm.PackageManagerServiceUtils; import com.android.server.pm.PackageSetting; @@ -902,16 +902,6 @@ public class PermissionManagerService extends IPermissionManager.Stub { } private int checkPermissionImpl(String permName, String pkgName, int userId) { - try { - enforceCrossUserOrProfilePermission(Binder.getCallingUid(), userId, - false, false, "checkPermissionImpl"); - } catch (Exception e) { - Slog.e(TAG, "Invalid cross user access", e); - EventLog.writeEvent(0x534e4554, "153996875", "checkPermissionImpl", pkgName); - - throw e; - } - final AndroidPackage pkg = mPackageManagerInt.getPackage(pkgName); if (pkg == null) { return PackageManager.PERMISSION_DENIED; @@ -989,16 +979,6 @@ public class PermissionManagerService extends IPermissionManager.Stub { } private int checkUidPermissionImpl(String permName, int uid) { - try { - enforceCrossUserOrProfilePermission(Binder.getCallingUid(), UserHandle.getUserId(uid), - false, false, "checkUidPermissionImpl"); - } catch (Exception e) { - Slog.e(TAG, "Invalid cross user access", e); - EventLog.writeEvent(0x534e4554, "153996875", "checkUidPermissionImpl", uid); - - throw e; - } - final AndroidPackage pkg = mPackageManagerInt.getPackage(uid); return checkUidPermissionInternal(pkg, uid, permName); } @@ -2535,20 +2515,6 @@ public class PermissionManagerService extends IPermissionManager.Stub { return permission.computeGids(userId); } - @Nullable - private int[] getPackageGids(@NonNull String packageName, @UserIdInt int userId) { - final PackageSetting ps = mPackageManagerInt.getPackageSetting(packageName); - if (ps == null) { - return null; - } - final UidPermissionState uidState = getUidState(ps, userId); - if (uidState == null) { - Slog.e(TAG, "Missing permissions state for " + packageName + " and user " + userId); - return null; - } - return uidState.computeGids(userId); - } - /** * Restore the permission state for a package. * @@ -4529,7 +4495,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { } final int callingUserId = UserHandle.getUserId(callingUid); if (hasCrossUserPermission( - Binder.getCallingPid(), callingUid, callingUserId, userId, requireFullPermission, + callingUid, callingUserId, userId, requireFullPermission, requirePermissionWhenSameUser)) { return; } @@ -4556,79 +4522,53 @@ public class PermissionManagerService extends IPermissionManager.Stub { private void enforceCrossUserOrProfilePermission(int callingUid, @UserIdInt int userId, boolean requireFullPermission, boolean checkShell, String message) { - int callingPid = Binder.getCallingPid(); - final int callingUserId = UserHandle.getUserId(callingUid); - if (userId < 0) { throw new IllegalArgumentException("Invalid userId " + userId); } - - if (callingUserId == userId) { + if (checkShell) { + PackageManagerServiceUtils.enforceShellRestriction(mUserManagerInt, + UserManager.DISALLOW_DEBUGGING_FEATURES, callingUid, userId); + } + final int callingUserId = UserHandle.getUserId(callingUid); + if (hasCrossUserPermission(callingUid, callingUserId, userId, requireFullPermission, + /*requirePermissionWhenSameUser= */ false)) { return; } - - // Prevent endless loop between when checking permission while checking a permission - if (callingPid == ActivityManagerService.MY_PID) { + final boolean isSameProfileGroup = isSameProfileGroup(callingUserId, userId); + if (isSameProfileGroup && PermissionChecker.checkPermissionForPreflight( + mContext, + android.Manifest.permission.INTERACT_ACROSS_PROFILES, + PermissionChecker.PID_UNKNOWN, + callingUid, + mPackageManagerInt.getPackage(callingUid).getPackageName()) + == PermissionChecker.PERMISSION_GRANTED) { return; } - - long token = Binder.clearCallingIdentity(); - try { - if (checkShell) { - PackageManagerServiceUtils.enforceShellRestriction(mUserManagerInt, - UserManager.DISALLOW_DEBUGGING_FEATURES, callingUid, userId); - } - if (hasCrossUserPermission(callingPid, callingUid, callingUserId, userId, - requireFullPermission, /*requirePermissionWhenSameUser= */ false)) { - return; - } - final boolean isSameProfileGroup = isSameProfileGroup(callingUserId, userId); - - if (isSameProfileGroup) { - AndroidPackage callingPkg = mPackageManagerInt.getPackage(callingUid); - String callingPkgName = null; - if (callingPkg != null) { - callingPkgName = callingPkg.getPackageName(); - } - - if (PermissionChecker.checkPermissionForPreflight( - mContext, - android.Manifest.permission.INTERACT_ACROSS_PROFILES, - PermissionChecker.PID_UNKNOWN, - callingUid, - callingPkgName) - == PermissionChecker.PERMISSION_GRANTED) { - return; - } - } - String errorMessage = buildInvalidCrossUserOrProfilePermissionMessage( callingUid, userId, message, requireFullPermission, isSameProfileGroup); Slog.w(TAG, errorMessage); throw new SecurityException(errorMessage); - } finally { - Binder.restoreCallingIdentity(token); - } } - private boolean hasCrossUserPermission(int callingPid, int callingUid, int callingUserId, - int userId, boolean requireFullPermission, boolean requirePermissionWhenSameUser) { + private boolean hasCrossUserPermission( + int callingUid, int callingUserId, int userId, boolean requireFullPermission, + boolean requirePermissionWhenSameUser) { if (!requirePermissionWhenSameUser && userId == callingUserId) { return true; } if (callingUid == Process.SYSTEM_UID || callingUid == Process.ROOT_UID) { return true; } - - if (!requireFullPermission) { - if (mContext.checkPermission(android.Manifest.permission.INTERACT_ACROSS_USERS, - callingPid, callingUid) == PackageManager.PERMISSION_GRANTED) { - return true; - } + if (requireFullPermission) { + return hasPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL); } + return hasPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) + || hasPermission(Manifest.permission.INTERACT_ACROSS_USERS); + } - return mContext.checkPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL, - callingPid, callingUid) == PackageManager.PERMISSION_GRANTED; + private boolean hasPermission(String permission) { + return mContext.checkCallingOrSelfPermission(permission) + == PackageManager.PERMISSION_GRANTED; } private boolean isSameProfileGroup(@UserIdInt int callerUserId, @UserIdInt int userId) { @@ -4813,7 +4753,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { } } - private void removeAppState(int appId) { + private void removeAppIdState(@AppIdInt int appId) { synchronized (mLock) { final int[] userIds = mState.getUserIds(); for (final int userId : userIds) { @@ -4827,7 +4767,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { final int[] userIds = getAllUserIds(); mPackageManagerInt.forEachPackageSetting(ps -> { final int appId = ps.getAppId(); - final PermissionsState permissionsState = ps.getPermissionsState(); + final AppIdPermissionState appIdState = ps.getPermissionsState(); synchronized (mLock) { for (final int userId : userIds) { @@ -4836,25 +4776,22 @@ public class PermissionManagerService extends IPermissionManager.Stub { userState.setInstallPermissionsFixed(ps.name, ps.areInstallPermissionsFixed()); final UidPermissionState uidState = userState.getOrCreateUidState(appId); uidState.reset(); - uidState.setGlobalGids(permissionsState.getGlobalGids()); - uidState.setMissing(permissionsState.isMissing(userId)); + uidState.setMissing(appIdState.isMissing(userId)); readStateFromPermissionStates(uidState, - permissionsState.getInstallPermissionStates(), false); + appIdState.getInstallPermissionStates(), false); readStateFromPermissionStates(uidState, - permissionsState.getRuntimePermissionStates(userId), true); + appIdState.getRuntimePermissionStates(userId), true); } } }); } private void readStateFromPermissionStates(@NonNull UidPermissionState uidState, - @NonNull List<PermissionsState.PermissionState> permissionStates, boolean isRuntime) { - final int permissionStatesSize = permissionStates.size(); - for (int i = 0; i < permissionStatesSize; i++) { - final PermissionsState.PermissionState permissionState = permissionStates.get(i); - final BasePermission permission = permissionState.getPermission(); - uidState.putPermissionState(permission, isRuntime, permissionState.isGranted(), - permissionState.getFlags()); + @NonNull Collection<AppIdPermissionState.PermissionState> permissionStates, + boolean isRuntime) { + for (final AppIdPermissionState.PermissionState permissionState : permissionStates) { + uidState.putPermissionState(permissionState.getPermission(), isRuntime, + permissionState.isGranted(), permissionState.getFlags()); } } @@ -4862,8 +4799,8 @@ public class PermissionManagerService extends IPermissionManager.Stub { final int[] userIds = mState.getUserIds(); mPackageManagerInt.forEachPackageSetting(ps -> { ps.setInstallPermissionsFixed(false); - final PermissionsState permissionsState = ps.getPermissionsState(); - permissionsState.reset(); + final AppIdPermissionState appIdState = ps.getPermissionsState(); + appIdState.reset(); final int appId = ps.getAppId(); synchronized (mLock) { @@ -4885,27 +4822,21 @@ public class PermissionManagerService extends IPermissionManager.Stub { continue; } - permissionsState.setGlobalGids(uidState.getGlobalGids()); - permissionsState.setMissing(uidState.isMissing(), userId); + appIdState.setMissing(uidState.isMissing(), userId); final List<PermissionState> permissionStates = uidState.getPermissionStates(); final int permissionStatesSize = permissionStates.size(); for (int i = 0; i < permissionStatesSize; i++) { final PermissionState permissionState = permissionStates.get(i); - final BasePermission permission = permissionState.getPermission(); - if (permissionState.isGranted()) { - if (permissionState.isRuntime()) { - permissionsState.grantRuntimePermission(permission, userId); - } else { - permissionsState.grantInstallPermission(permission); - } - } - final int flags = permissionState.getFlags(); - if (flags != 0) { - final int flagsUserId = permissionState.isRuntime() ? userId - : UserHandle.USER_ALL; - permissionsState.updatePermissionFlags(permission, flagsUserId, flags, - flags); + final AppIdPermissionState.PermissionState legacyPermissionState = + new AppIdPermissionState.PermissionState( + permissionState.getPermission(), + permissionState.isGranted(), permissionState.getFlags()); + if (permissionState.isRuntime()) { + appIdState.putRuntimePermissionState(legacyPermissionState, + userId); + } else { + appIdState.putInstallPermissionState(legacyPermissionState); } } } @@ -4913,6 +4844,48 @@ public class PermissionManagerService extends IPermissionManager.Stub { }); } + @NonNull + private AppIdPermissionState getAppIdPermissionState(@AppIdInt int appId) { + final AppIdPermissionState appIdState = new AppIdPermissionState(); + final int[] userIds = mState.getUserIds(); + for (final int userId : userIds) { + final UidPermissionState uidState = getUidState(appId, userId); + if (uidState == null) { + Slog.e(TAG, "Missing permissions state for app ID " + appId + " and user ID " + + userId); + continue; + } + + final List<PermissionState> permissionStates = uidState.getPermissionStates(); + final int permissionStatesSize = permissionStates.size(); + for (int i = 0; i < permissionStatesSize; i++) { + final PermissionState permissionState = permissionStates.get(i); + + final AppIdPermissionState.PermissionState legacyPermissionState = + new AppIdPermissionState.PermissionState(permissionState.getPermission(), + permissionState.isGranted(), permissionState.getFlags()); + if (permissionState.isRuntime()) { + appIdState.putRuntimePermissionState(legacyPermissionState, userId); + } else if (userId == UserHandle.USER_SYSTEM) { + appIdState.putInstallPermissionState(legacyPermissionState); + } + } + } + return appIdState; + } + + @NonNull + private int[] getGidsForUid(int uid) { + final int appId = UserHandle.getAppId(uid); + final int userId = UserHandle.getUserId(uid); + final UidPermissionState uidState = getUidState(appId, userId); + if (uidState == null) { + Slog.e(TAG, "Missing permissions state for app ID " + appId + " and user ID " + userId); + return EMPTY_INT_ARRAY; + } + return uidState.computeGids(userId); + } + private class PermissionManagerServiceInternalImpl extends PermissionManagerServiceInternal { @Override public void systemReady() { @@ -4957,8 +4930,8 @@ public class PermissionManagerService extends IPermissionManager.Stub { PermissionManagerService.this.onUserRemoved(userId); } @Override - public void removePermissionsStateTEMP(int appId) { - PermissionManagerService.this.removeAppState(appId); + public void removeAppIdStateTEMP(@AppIdInt int appId) { + PermissionManagerService.this.removeAppIdState(appId); } @Override @UserIdInt @@ -4978,11 +4951,6 @@ public class PermissionManagerService extends IPermissionManager.Stub { public int[] getPermissionGids(@NonNull String permissionName, @UserIdInt int userId) { return PermissionManagerService.this.getPermissionGids(permissionName, userId); } - @Nullable - @Override - public int[] getPackageGids(@NonNull String packageName, @UserIdInt int userId) { - return PermissionManagerService.this.getPackageGids(packageName, userId); - } @Override public void grantRequestedRuntimePermissions(AndroidPackage pkg, int[] userIds, String[] grantedPermissions, int callingUid) { @@ -5310,6 +5278,16 @@ public class PermissionManagerService extends IPermissionManager.Stub { } } } + + @NonNull + public AppIdPermissionState getAppIdPermissionState(@AppIdInt int appId) { + return PermissionManagerService.this.getAppIdPermissionState(appId); + } + + @NonNull + public int[] getGidsForUid(int uid) { + return PermissionManagerService.this.getGidsForUid(uid); + } } private static final class OnPermissionChangeListeners extends Handler { diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java index 6d9bd2a1b30d..5ea3458fcbfa 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java @@ -37,8 +37,8 @@ import java.util.function.Consumer; * * TODO: Should be merged into PermissionManagerInternal, but currently uses internal classes. */ -public abstract class PermissionManagerServiceInternal extends PermissionManagerInternal { - +public abstract class PermissionManagerServiceInternal extends PermissionManagerInternal + implements LegacyPermissionDataProvider { /** * Provider for package names. */ @@ -288,13 +288,13 @@ public abstract class PermissionManagerServiceInternal extends PermissionManager public abstract void onUserRemoved(@UserIdInt int userId); /** - * Remove the {@code PermissionsState} associated with an app ID, called the same time as the + * Remove the permission state associated with an app ID, called the same time as the * removal of a {@code PackageSetitng}. * * TODO(zhanghai): This is a temporary method before we figure out a way to get notified of app * ID removal via API. */ - public abstract void removePermissionsStateTEMP(int appId); + public abstract void removeAppIdStateTEMP(@AppIdInt int appId); /** * Update the shared user setting when a package with a shared user id is removed. The gids @@ -324,12 +324,6 @@ public abstract class PermissionManagerServiceInternal extends PermissionManager @Nullable public abstract int[] getPermissionGids(@NonNull String permissionName, @UserIdInt int userId); - /** - * Get the GIDs computed from the permission state of a package. - */ - @Nullable - public abstract int[] getPackageGids(@NonNull String packageName, @UserIdInt int userId); - /** Retrieve the packages that have requested the given app op permission */ public abstract @Nullable String[] getAppOpPermissionPackages( @NonNull String permName, int callingUid); diff --git a/services/core/java/com/android/server/pm/permission/PermissionsState.java b/services/core/java/com/android/server/pm/permission/PermissionsState.java deleted file mode 100644 index 4fb2d5fc200e..000000000000 --- a/services/core/java/com/android/server/pm/permission/PermissionsState.java +++ /dev/null @@ -1,970 +0,0 @@ -/* - * Copyright (C) 2015 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.permission; - -import android.annotation.Nullable; -import android.annotation.UserIdInt; -import android.content.pm.PackageManager; -import android.os.UserHandle; -import android.util.ArrayMap; -import android.util.ArraySet; -import android.util.SparseArray; -import android.util.SparseBooleanArray; - -import com.android.internal.annotations.GuardedBy; -import com.android.internal.util.ArrayUtils; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.Objects; -import java.util.Set; - -/** - * This class encapsulates the permissions for a package or a shared user. - * <p> - * There are two types of permissions: install (granted at installation) - * and runtime (granted at runtime). Install permissions are granted to - * all device users while runtime permissions are granted explicitly to - * specific users. - * </p> - * <p> - * The permissions are kept on a per device user basis. For example, an - * application may have some runtime permissions granted under the device - * owner but not granted under the secondary user. - * <p> - * This class is also responsible for keeping track of the Linux gids per - * user for a package or a shared user. The gids are computed as a set of - * the gids for all granted permissions' gids on a per user basis. - * </p> - */ -public final class PermissionsState { - - /** The permission operation failed. */ - public static final int PERMISSION_OPERATION_FAILURE = -1; - - /** The permission operation succeeded and no gids changed. */ - public static final int PERMISSION_OPERATION_SUCCESS = 0; - - /** The permission operation succeeded and gids changed. */ - public static final int PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED = 1; - - private static final int[] NO_GIDS = {}; - - private final Object mLock = new Object(); - - @GuardedBy("mLock") - private ArrayMap<String, PermissionData> mPermissions; - - private int[] mGlobalGids = NO_GIDS; - - @Nullable - private SparseBooleanArray mMissing; - - private SparseBooleanArray mPermissionReviewRequired; - - public PermissionsState() { - /* do nothing */ - } - - public PermissionsState(PermissionsState prototype) { - copyFrom(prototype); - } - - public int[] getGlobalGids() { - return mGlobalGids; - } - - /** - * Sets the global gids, applicable to all users. - * - * @param globalGids The global gids. - */ - public void setGlobalGids(int[] globalGids) { - if (!ArrayUtils.isEmpty(globalGids)) { - mGlobalGids = Arrays.copyOf(globalGids, globalGids.length); - } - } - - private static void invalidateCache() { - PackageManager.invalidatePackageInfoCache(); - } - - /** - * Initialized this instance from another one. - * - * @param other The other instance. - */ - public void copyFrom(PermissionsState other) { - if (other == this) { - return; - } - - synchronized (mLock) { - if (mPermissions != null) { - if (other.mPermissions == null) { - mPermissions = null; - } else { - mPermissions.clear(); - } - } - if (other.mPermissions != null) { - if (mPermissions == null) { - mPermissions = new ArrayMap<>(); - } - final int permissionCount = other.mPermissions.size(); - for (int i = 0; i < permissionCount; i++) { - String name = other.mPermissions.keyAt(i); - PermissionData permissionData = other.mPermissions.valueAt(i); - mPermissions.put(name, new PermissionData(permissionData)); - } - } - } - - mGlobalGids = NO_GIDS; - if (other.mGlobalGids != NO_GIDS) { - mGlobalGids = Arrays.copyOf(other.mGlobalGids, - other.mGlobalGids.length); - } - - if (mMissing != null) { - if (other.mMissing == null) { - mMissing = null; - } else { - mMissing.clear(); - } - } - if (other.mMissing != null) { - if (mMissing == null) { - mMissing = new SparseBooleanArray(); - } - final int missingSize = other.mMissing.size(); - for (int i = 0; i < missingSize; i++) { - mMissing.put(other.mMissing.keyAt(i), other.mMissing.valueAt(i)); - } - } - - if (mPermissionReviewRequired != null) { - if (other.mPermissionReviewRequired == null) { - mPermissionReviewRequired = null; - } else { - mPermissionReviewRequired.clear(); - } - } - if (other.mPermissionReviewRequired != null) { - if (mPermissionReviewRequired == null) { - mPermissionReviewRequired = new SparseBooleanArray(); - } - final int userCount = other.mPermissionReviewRequired.size(); - for (int i = 0; i < userCount; i++) { - final boolean reviewRequired = other.mPermissionReviewRequired.valueAt(i); - mPermissionReviewRequired.put(other.mPermissionReviewRequired.keyAt(i), - reviewRequired); - } - } - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - final PermissionsState other = (PermissionsState) obj; - - synchronized (mLock) { - if (mPermissions == null) { - if (other.mPermissions != null) { - return false; - } - } else if (!mPermissions.equals(other.mPermissions)) { - return false; - } - } - - if (!Objects.equals(mMissing, other.mMissing)) { - return false; - } - - if (mPermissionReviewRequired == null) { - if (other.mPermissionReviewRequired != null) { - return false; - } - } else if (!mPermissionReviewRequired.equals(other.mPermissionReviewRequired)) { - return false; - } - return Arrays.equals(mGlobalGids, other.mGlobalGids); - } - - /** - * Check whether the permissions state is missing for a user. This can happen if permission - * state is rolled back and we'll need to generate a reasonable default state to keep the app - * usable. - */ - public boolean isMissing(@UserIdInt int userId) { - return mMissing != null && mMissing.get(userId); - } - - /** - * Set whether the permissions state is missing for a user. This can happen if permission state - * is rolled back and we'll need to generate a reasonable default state to keep the app usable. - */ - public void setMissing(boolean missing, @UserIdInt int userId) { - if (missing) { - if (mMissing == null) { - mMissing = new SparseBooleanArray(); - } - mMissing.put(userId, true); - } else { - if (mMissing != null) { - mMissing.delete(userId); - if (mMissing.size() == 0) { - mMissing = null; - } - } - } - } - - public boolean isPermissionReviewRequired(int userId) { - return mPermissionReviewRequired != null && mPermissionReviewRequired.get(userId); - } - - /** - * Grant an install permission. - * - * @param permission The permission to grant. - * @return The operation result which is either {@link #PERMISSION_OPERATION_SUCCESS}, - * or {@link #PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED}, or {@link - * #PERMISSION_OPERATION_FAILURE}. - */ - public int grantInstallPermission(BasePermission permission) { - return grantPermission(permission, UserHandle.USER_ALL); - } - - /** - * Revoke an install permission. - * - * @param permission The permission to revoke. - * @return The operation result which is either {@link #PERMISSION_OPERATION_SUCCESS}, - * or {@link #PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED}, or {@link - * #PERMISSION_OPERATION_FAILURE}. - */ - public int revokeInstallPermission(BasePermission permission) { - return revokePermission(permission, UserHandle.USER_ALL); - } - - /** - * Grant a runtime permission for a given device user. - * - * @param permission The permission to grant. - * @param userId The device user id. - * @return The operation result which is either {@link #PERMISSION_OPERATION_SUCCESS}, - * or {@link #PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED}, or {@link - * #PERMISSION_OPERATION_FAILURE}. - */ - public int grantRuntimePermission(BasePermission permission, int userId) { - enforceValidUserId(userId); - if (userId == UserHandle.USER_ALL) { - return PERMISSION_OPERATION_FAILURE; - } - return grantPermission(permission, userId); - } - - /** - * Revoke a runtime permission for a given device user. - * - * @param permission The permission to revoke. - * @param userId The device user id. - * @return The operation result which is either {@link #PERMISSION_OPERATION_SUCCESS}, - * or {@link #PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED}, or {@link - * #PERMISSION_OPERATION_FAILURE}. - */ - public int revokeRuntimePermission(BasePermission permission, int userId) { - enforceValidUserId(userId); - if (userId == UserHandle.USER_ALL) { - return PERMISSION_OPERATION_FAILURE; - } - return revokePermission(permission, userId); - } - - /** - * Gets whether this state has a given runtime permission for a - * given device user id. - * - * @param name The permission name. - * @param userId The device user id. - * @return Whether this state has the permission. - */ - public boolean hasRuntimePermission(String name, int userId) { - enforceValidUserId(userId); - return !hasInstallPermission(name) && hasPermission(name, userId); - } - - /** - * Gets whether this state has a given install permission. - * - * @param name The permission name. - * @return Whether this state has the permission. - */ - public boolean hasInstallPermission(String name) { - return hasPermission(name, UserHandle.USER_ALL); - } - - /** - * Gets whether the state has a given permission for the specified - * user, regardless if this is an install or a runtime permission. - * - * @param name The permission name. - * @param userId The device user id. - * @return Whether the user has the permission. - */ - public boolean hasPermission(String name, int userId) { - enforceValidUserId(userId); - - synchronized (mLock) { - if (mPermissions == null) { - return false; - } - PermissionData permissionData = mPermissions.get(name); - - return permissionData != null && permissionData.isGranted(userId); - } - - } - - /** - * Returns whether the state has any known request for the given permission name, - * whether or not it has been granted. - */ - public boolean hasRequestedPermission(ArraySet<String> names) { - synchronized (mLock) { - if (mPermissions == null) { - return false; - } - for (int i=names.size()-1; i>=0; i--) { - if (mPermissions.get(names.valueAt(i)) != null) { - return true; - } - } - } - - return false; - } - - /** - * Returns whether the state has any known request for the given permission name, - * whether or not it has been granted. - */ - public boolean hasRequestedPermission(String name) { - return mPermissions != null && (mPermissions.get(name) != null); - } - /** - * Gets all permissions for a given device user id regardless if they - * are install time or runtime permissions. - * - * @param userId The device user id. - * @return The permissions or an empty set. - */ - public Set<String> getPermissions(int userId) { - enforceValidUserId(userId); - - synchronized (mLock) { - if (mPermissions == null) { - return Collections.emptySet(); - } - - Set<String> permissions = new ArraySet<>(mPermissions.size()); - - final int permissionCount = mPermissions.size(); - for (int i = 0; i < permissionCount; i++) { - String permission = mPermissions.keyAt(i); - - if (hasInstallPermission(permission)) { - permissions.add(permission); - continue; - } - - if (userId != UserHandle.USER_ALL) { - if (hasRuntimePermission(permission, userId)) { - permissions.add(permission); - } - } - } - - return permissions; - } - } - - /** - * Gets the state for an install permission or null if no such. - * - * @param name The permission name. - * @return The permission state. - */ - public PermissionState getInstallPermissionState(String name) { - return getPermissionState(name, UserHandle.USER_ALL); - } - - /** - * Gets the state for a runtime permission or null if no such. - * - * @param name The permission name. - * @param userId The device user id. - * @return The permission state. - */ - public PermissionState getRuntimePermissionState(String name, int userId) { - enforceValidUserId(userId); - return getPermissionState(name, userId); - } - - /** - * Gets all install permission states. - * - * @return The permission states or an empty set. - */ - public List<PermissionState> getInstallPermissionStates() { - return getPermissionStatesInternal(UserHandle.USER_ALL); - } - - /** - * Gets all runtime permission states. - * - * @return The permission states or an empty set. - */ - public List<PermissionState> getRuntimePermissionStates(int userId) { - enforceValidUserId(userId); - return getPermissionStatesInternal(userId); - } - - /** - * Gets the flags for a permission regardless if it is install or - * runtime permission. - * - * @param name The permission name. - * @return The permission state or null if no such. - */ - public int getPermissionFlags(String name, int userId) { - PermissionState installPermState = getInstallPermissionState(name); - if (installPermState != null) { - return installPermState.getFlags(); - } - PermissionState runtimePermState = getRuntimePermissionState(name, userId); - if (runtimePermState != null) { - return runtimePermState.getFlags(); - } - return 0; - } - - /** - * Update the flags associated with a given permission. - * @param permission The permission whose flags to update. - * @param userId The user for which to update. - * @param flagMask Mask for which flags to change. - * @param flagValues New values for the mask flags. - * @return Whether the permission flags changed. - */ - public boolean updatePermissionFlags(BasePermission permission, int userId, - int flagMask, int flagValues) { - enforceValidUserId(userId); - - final boolean mayChangeFlags = flagValues != 0 || flagMask != 0; - - synchronized (mLock) { - if (mPermissions == null) { - if (!mayChangeFlags) { - return false; - } - ensurePermissionData(permission); - } - } - - PermissionData permissionData = null; - synchronized (mLock) { - permissionData = mPermissions.get(permission.getName()); - } - - if (permissionData == null) { - if (!mayChangeFlags) { - return false; - } - permissionData = ensurePermissionData(permission); - } - - final int oldFlags = permissionData.getFlags(userId); - - final boolean updated = permissionData.updateFlags(userId, flagMask, flagValues); - if (updated) { - final int newFlags = permissionData.getFlags(userId); - if ((oldFlags & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) == 0 - && (newFlags & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) != 0) { - if (mPermissionReviewRequired == null) { - mPermissionReviewRequired = new SparseBooleanArray(); - } - mPermissionReviewRequired.put(userId, true); - } else if ((oldFlags & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) != 0 - && (newFlags & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) == 0) { - if (mPermissionReviewRequired != null && !hasPermissionRequiringReview(userId)) { - mPermissionReviewRequired.delete(userId); - if (mPermissionReviewRequired.size() <= 0) { - mPermissionReviewRequired = null; - } - } - } - } - return updated; - } - - private boolean hasPermissionRequiringReview(int userId) { - synchronized (mLock) { - final int permissionCount = mPermissions.size(); - for (int i = 0; i < permissionCount; i++) { - final PermissionData permission = mPermissions.valueAt(i); - if ((permission.getFlags(userId) - & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) != 0) { - return true; - } - } - } - - return false; - } - - public boolean updatePermissionFlagsForAllPermissions( - int userId, int flagMask, int flagValues) { - enforceValidUserId(userId); - - synchronized (mLock) { - if (mPermissions == null) { - return false; - } - boolean changed = false; - final int permissionCount = mPermissions.size(); - for (int i = 0; i < permissionCount; i++) { - PermissionData permissionData = mPermissions.valueAt(i); - changed |= permissionData.updateFlags(userId, flagMask, flagValues); - } - - return changed; - } - } - - /** - * Compute the Linux gids for a given device user from the permissions - * granted to this user. Note that these are computed to avoid additional - * state as they are rarely accessed. - * - * @param userId The device user id. - * @return The gids for the device user. - */ - public int[] computeGids(int userId) { - enforceValidUserId(userId); - - int[] gids = mGlobalGids; - - synchronized (mLock) { - if (mPermissions != null) { - final int permissionCount = mPermissions.size(); - for (int i = 0; i < permissionCount; i++) { - String permission = mPermissions.keyAt(i); - if (!hasPermission(permission, userId)) { - continue; - } - PermissionData permissionData = mPermissions.valueAt(i); - final int[] permGids = permissionData.computeGids(userId); - if (permGids != NO_GIDS) { - gids = appendInts(gids, permGids); - } - } - } - } - - return gids; - } - - /** - * Compute the Linux gids for all device users from the permissions - * granted to these users. - * - * @return The gids for all device users. - */ - public int[] computeGids(int[] userIds) { - int[] gids = mGlobalGids; - - for (int userId : userIds) { - final int[] userGids = computeGids(userId); - gids = appendInts(gids, userGids); - } - - return gids; - } - - /** - * Resets the internal state of this object. - */ - public void reset() { - mGlobalGids = NO_GIDS; - - synchronized (mLock) { - mPermissions = null; - invalidateCache(); - } - - mMissing = null; - mPermissionReviewRequired = null; - } - - private PermissionState getPermissionState(String name, int userId) { - synchronized (mLock) { - if (mPermissions == null) { - return null; - } - PermissionData permissionData = mPermissions.get(name); - if (permissionData == null) { - return null; - } - - return permissionData.getPermissionState(userId); - } - } - - private List<PermissionState> getPermissionStatesInternal(int userId) { - enforceValidUserId(userId); - - synchronized (mLock) { - if (mPermissions == null) { - return Collections.emptyList(); - } - - List<PermissionState> permissionStates = new ArrayList<>(); - - final int permissionCount = mPermissions.size(); - for (int i = 0; i < permissionCount; i++) { - PermissionData permissionData = mPermissions.valueAt(i); - - PermissionState permissionState = permissionData.getPermissionState(userId); - if (permissionState != null) { - permissionStates.add(permissionState); - } - } - - return permissionStates; - } - } - - private int grantPermission(BasePermission permission, int userId) { - if (hasPermission(permission.getName(), userId)) { - return PERMISSION_OPERATION_SUCCESS; - } - - final boolean hasGids = !ArrayUtils.isEmpty(permission.computeGids(userId)); - final int[] oldGids = hasGids ? computeGids(userId) : NO_GIDS; - - PermissionData permissionData = ensurePermissionData(permission); - - if (!permissionData.grant(userId)) { - return PERMISSION_OPERATION_FAILURE; - } - - if (hasGids) { - final int[] newGids = computeGids(userId); - if (oldGids.length != newGids.length) { - return PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED; - } - } - - return PERMISSION_OPERATION_SUCCESS; - } - - private int revokePermission(BasePermission permission, int userId) { - final String permName = permission.getName(); - if (!hasPermission(permName, userId)) { - return PERMISSION_OPERATION_SUCCESS; - } - - final boolean hasGids = !ArrayUtils.isEmpty(permission.computeGids(userId)); - final int[] oldGids = hasGids ? computeGids(userId) : NO_GIDS; - - PermissionData permissionData = null; - synchronized (mLock) { - permissionData = mPermissions.get(permName); - } - - if (!permissionData.revoke(userId)) { - return PERMISSION_OPERATION_FAILURE; - } - - if (permissionData.isDefault()) { - ensureNoPermissionData(permName); - } - - if (hasGids) { - final int[] newGids = computeGids(userId); - if (oldGids.length != newGids.length) { - return PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED; - } - } - - return PERMISSION_OPERATION_SUCCESS; - } - - // TODO: fix this to use arraycopy and append all ints in one go - private static int[] appendInts(int[] current, int[] added) { - if (current != null && added != null) { - for (int guid : added) { - current = ArrayUtils.appendInt(current, guid); - } - } - return current; - } - - private static void enforceValidUserId(int userId) { - if (userId != UserHandle.USER_ALL && userId < 0) { - throw new IllegalArgumentException("Invalid userId:" + userId); - } - } - - private PermissionData ensurePermissionData(BasePermission permission) { - final String permName = permission.getName(); - - synchronized (mLock) { - if (mPermissions == null) { - mPermissions = new ArrayMap<>(); - } - PermissionData permissionData = mPermissions.get(permName); - if (permissionData == null) { - permissionData = new PermissionData(permission); - mPermissions.put(permName, permissionData); - } - return permissionData; - } - - } - - private void ensureNoPermissionData(String name) { - synchronized (mLock) { - if (mPermissions == null) { - return; - } - mPermissions.remove(name); - if (mPermissions.isEmpty()) { - mPermissions = null; - } - } - - } - - private static final class PermissionData { - - private final Object mLock = new Object(); - - private final BasePermission mPerm; - @GuardedBy("mLock") - private SparseArray<PermissionState> mUserStates = new SparseArray<>(); - - public PermissionData(BasePermission perm) { - mPerm = perm; - } - - public PermissionData(PermissionData other) { - this(other.mPerm); - - synchronized (mLock) { - final int otherStateCount = other.mUserStates.size(); - for (int i = 0; i < otherStateCount; i++) { - final int otherUserId = other.mUserStates.keyAt(i); - PermissionState otherState = other.mUserStates.valueAt(i); - mUserStates.put(otherUserId, new PermissionState(otherState)); - } - } - } - - public int[] computeGids(int userId) { - return mPerm.computeGids(userId); - } - - public boolean isGranted(int userId) { - synchronized (mLock) { - if (isInstallPermission()) { - userId = UserHandle.USER_ALL; - } - - PermissionState userState = mUserStates.get(userId); - if (userState == null) { - return false; - } - - return userState.mGranted; - } - } - - public boolean grant(int userId) { - synchronized (mLock) { - if (!isCompatibleUserId(userId)) { - return false; - } - - if (isGranted(userId)) { - return false; - } - - PermissionState userState = mUserStates.get(userId); - if (userState == null) { - userState = new PermissionState(mPerm); - mUserStates.put(userId, userState); - } - - userState.mGranted = true; - - invalidateCache(); - return true; - } - } - - public boolean revoke(int userId) { - synchronized (mLock) { - if (!isCompatibleUserId(userId)) { - return false; - } - - if (!isGranted(userId)) { - return false; - } - - PermissionState userState = mUserStates.get(userId); - userState.mGranted = false; - - if (userState.isDefault()) { - mUserStates.remove(userId); - } - - invalidateCache(); - return true; - } - } - - public PermissionState getPermissionState(int userId) { - synchronized (mLock) { - return mUserStates.get(userId); - } - } - - public int getFlags(int userId) { - synchronized (mLock) { - PermissionState userState = mUserStates.get(userId); - if (userState != null) { - return userState.mFlags; - } - return 0; - } - } - - public boolean isDefault() { - synchronized (mLock) { - return mUserStates.size() <= 0; - } - } - - public static boolean isInstallPermissionKey(int userId) { - return userId == UserHandle.USER_ALL; - } - - public boolean updateFlags(int userId, int flagMask, int flagValues) { - synchronized (mLock) { - if (isInstallPermission()) { - userId = UserHandle.USER_ALL; - } - - if (!isCompatibleUserId(userId)) { - return false; - } - - final int newFlags = flagValues & flagMask; - - // Okay to do before the modification because we hold the lock. - invalidateCache(); - - PermissionState userState = mUserStates.get(userId); - if (userState != null) { - final int oldFlags = userState.mFlags; - userState.mFlags = (userState.mFlags & ~flagMask) | newFlags; - if (userState.isDefault()) { - mUserStates.remove(userId); - } - return userState.mFlags != oldFlags; - } else if (newFlags != 0) { - userState = new PermissionState(mPerm); - userState.mFlags = newFlags; - mUserStates.put(userId, userState); - return true; - } - - return false; - } - } - - private boolean isCompatibleUserId(int userId) { - return isDefault() || !(isInstallPermission() ^ isInstallPermissionKey(userId)); - } - - private boolean isInstallPermission() { - return mUserStates.size() == 1 - && mUserStates.get(UserHandle.USER_ALL) != null; - } - } - - public static final class PermissionState { - private final BasePermission mPermission; - private boolean mGranted; - private int mFlags; - - public PermissionState(BasePermission permission) { - mPermission = permission; - } - - public PermissionState(PermissionState other) { - mPermission = other.mPermission; - mGranted = other.mGranted; - mFlags = other.mFlags; - } - - public boolean isDefault() { - return !mGranted && mFlags == 0; - } - - public BasePermission getPermission() { - return mPermission; - } - - public String getName() { - return mPermission.getName(); - } - - public boolean isGranted() { - return mGranted; - } - - public int getFlags() { - return mFlags; - } - } -} diff --git a/services/core/java/com/android/server/pm/permission/TEST_MAPPING b/services/core/java/com/android/server/pm/permission/TEST_MAPPING index 65dc320eadc2..c0d71ac26853 100644 --- a/services/core/java/com/android/server/pm/permission/TEST_MAPPING +++ b/services/core/java/com/android/server/pm/permission/TEST_MAPPING @@ -18,24 +18,21 @@ ] }, { - "name": "CtsPermission2TestCases", + "name": "CtsAppSecurityHostTestCases", "options": [ { - "include-filter": "android.permission2.cts.RestrictedPermissionsTest" - }, - { - "include-filter": "android.permission.cts.PermissionMaxSdkVersionTest" + "include-filter": "android.appsecurity.cts.AppSecurityTests#rebootWithDuplicatePermission" } ] }, { - "name": "CtsPermissionHostTestCases" - }, - { - "name": "CtsAppSecurityHostTestCases", + "name": "CtsPermission2TestCases", "options": [ { - "include-filter": "android.appsecurity.cts.AppSecurityTests#rebootWithDuplicatePermission" + "include-filter": "android.permission2.cts.RestrictedPermissionsTest" + }, + { + "include-filter": "android.permission.cts.PermissionMaxSdkVersionTest" } ] }, diff --git a/services/core/java/com/android/server/soundtrigger_middleware/ConversionUtil.java b/services/core/java/com/android/server/soundtrigger_middleware/ConversionUtil.java index 1d31285ed9c7..de8823c4b7f3 100644 --- a/services/core/java/com/android/server/soundtrigger_middleware/ConversionUtil.java +++ b/services/core/java/com/android/server/soundtrigger_middleware/ConversionUtil.java @@ -40,7 +40,10 @@ import android.media.soundtrigger_middleware.SoundModel; import android.media.soundtrigger_middleware.SoundModelType; import android.media.soundtrigger_middleware.SoundTriggerModuleProperties; import android.os.HidlMemoryUtil; +import android.os.ParcelFileDescriptor; +import java.io.FileDescriptor; +import java.io.IOException; import java.util.regex.Matcher; /** @@ -196,8 +199,18 @@ class ConversionUtil { hidlModel.header.type = aidl2hidlSoundModelType(aidlModel.type); hidlModel.header.uuid = aidl2hidlUuid(aidlModel.uuid); hidlModel.header.vendorUuid = aidl2hidlUuid(aidlModel.vendorUuid); - hidlModel.data = HidlMemoryUtil.fileDescriptorToHidlMemory(aidlModel.data, - aidlModel.dataSize); + + // Extract a dup of the underlying FileDescriptor out of aidlModel.data without changing + // the original. + FileDescriptor fd = new FileDescriptor(); + try { + ParcelFileDescriptor dup = aidlModel.data.dup(); + fd.setInt$(dup.detachFd()); + hidlModel.data = HidlMemoryUtil.fileDescriptorToHidlMemory(fd, aidlModel.dataSize); + } catch (IOException e) { + throw new RuntimeException(e); + } + return hidlModel; } diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index e8e4059af324..a9f06813faf5 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -700,11 +700,15 @@ class ActivityStarter { mService.updateConfigurationLocked(mRequest.globalConfig, null, false); } + // The original options may have additional info about metrics. The mOptions is not + // used here because it may be cleared in setTargetStackIfNeeded. + final ActivityOptions originalOptions = mRequest.activityOptions != null + ? mRequest.activityOptions.getOriginalOptions() : null; // Notify ActivityMetricsLogger that the activity has launched. // ActivityMetricsLogger will then wait for the windows to be drawn and populate // WaitResult. mSupervisor.getActivityMetricsLogger().notifyActivityLaunched(launchingState, res, - mLastStartActivityRecord, mOptions); + mLastStartActivityRecord, originalOptions); return getExternalResult(mRequest.waitResult == null ? res : waitForResult(res, mLastStartActivityRecord)); } diff --git a/services/core/java/com/android/server/wm/BLASTSyncEngine.java b/services/core/java/com/android/server/wm/BLASTSyncEngine.java index 6305fda4924c..958a7a8f07f8 100644 --- a/services/core/java/com/android/server/wm/BLASTSyncEngine.java +++ b/services/core/java/com/android/server/wm/BLASTSyncEngine.java @@ -16,9 +16,13 @@ package com.android.server.wm; +import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_SYNC_ENGINE; + import android.util.ArrayMap; import android.util.ArraySet; +import com.android.internal.protolog.common.ProtoLog; + import java.util.Set; /** @@ -63,25 +67,38 @@ class BLASTSyncEngine { private void tryFinish() { if (mRemainingTransactions == 0 && mReady) { + ProtoLog.v(WM_DEBUG_SYNC_ENGINE, "SyncSet{%x:%d} Finished. Reporting %d " + + "containers to %s", BLASTSyncEngine.this.hashCode(), mSyncId, + mWindowContainersReady.size(), mListener); mListener.onTransactionReady(mSyncId, mWindowContainersReady); mPendingSyncs.remove(mSyncId); } } - public void onTransactionReady(int mSyncId, Set<WindowContainer> windowContainersReady) { + public void onTransactionReady(int syncId, Set<WindowContainer> windowContainersReady) { mRemainingTransactions--; + ProtoLog.v(WM_DEBUG_SYNC_ENGINE, "SyncSet{%x:%d} Child ready, now ready=%b" + + " and waiting on %d transactions", BLASTSyncEngine.this.hashCode(), mSyncId, + mReady, mRemainingTransactions); mWindowContainersReady.addAll(windowContainersReady); tryFinish(); } void setReady() { + ProtoLog.v(WM_DEBUG_SYNC_ENGINE, "SyncSet{%x:%d} Set ready", + BLASTSyncEngine.this.hashCode(), mSyncId); mReady = true; tryFinish(); } boolean addToSync(WindowContainer wc) { + ProtoLog.v(WM_DEBUG_SYNC_ENGINE, "SyncSet{%x:%d} Trying to add %s", + BLASTSyncEngine.this.hashCode(), mSyncId, wc); if (wc.prepareForSync(this, mSyncId)) { mRemainingTransactions++; + ProtoLog.v(WM_DEBUG_SYNC_ENGINE, "SyncSet{%x:%d} Added %s. now waiting " + + "on %d transactions", BLASTSyncEngine.this.hashCode(), mSyncId, wc, + mRemainingTransactions); return true; } return false; @@ -105,6 +122,7 @@ class BLASTSyncEngine { final int id = mNextSyncId++; final SyncState s = new SyncState(listener, id); mPendingSyncs.put(id, s); + ProtoLog.v(WM_DEBUG_SYNC_ENGINE, "SyncSet{%x:%d} Start for %s", hashCode(), id, listener); return id; } diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java index 572c9b306047..6fffde14cf7d 100644 --- a/services/core/java/com/android/server/wm/DisplayPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayPolicy.java @@ -649,7 +649,7 @@ public class DisplayPolicy { mRefreshRatePolicy = new RefreshRatePolicy(mService, mDisplayContent.getDisplayInfo(), - mService.mHighRefreshRateBlacklist); + mService.mHighRefreshRateDenylist); mGestureNavigationSettingsObserver = new GestureNavigationSettingsObserver(mHandler, mContext, () -> { diff --git a/services/core/java/com/android/server/wm/HighRefreshRateBlacklist.java b/services/core/java/com/android/server/wm/HighRefreshRateDenylist.java index aac6b2544c4f..cdc14cd11228 100644 --- a/services/core/java/com/android/server/wm/HighRefreshRateBlacklist.java +++ b/services/core/java/com/android/server/wm/HighRefreshRateDenylist.java @@ -34,62 +34,62 @@ import java.io.PrintWriter; /** * A Denylist for packages that should force the display out of high refresh rate. */ -class HighRefreshRateBlacklist { +class HighRefreshRateDenylist { - private final ArraySet<String> mBlacklistedPackages = new ArraySet<>(); + private final ArraySet<String> mDenylistedPackages = new ArraySet<>(); @NonNull - private final String[] mDefaultBlacklist; + private final String[] mDefaultDenylist; private final Object mLock = new Object(); private DeviceConfigInterface mDeviceConfig; private OnPropertiesChangedListener mListener = new OnPropertiesChangedListener(); - static HighRefreshRateBlacklist create(@NonNull Resources r) { - return new HighRefreshRateBlacklist(r, DeviceConfigInterface.REAL); + static HighRefreshRateDenylist create(@NonNull Resources r) { + return new HighRefreshRateDenylist(r, DeviceConfigInterface.REAL); } @VisibleForTesting - HighRefreshRateBlacklist(Resources r, DeviceConfigInterface deviceConfig) { - mDefaultBlacklist = r.getStringArray(R.array.config_highRefreshRateBlacklist); + HighRefreshRateDenylist(Resources r, DeviceConfigInterface deviceConfig) { + mDefaultDenylist = r.getStringArray(R.array.config_highRefreshRateBlacklist); mDeviceConfig = deviceConfig; mDeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_DISPLAY_MANAGER, BackgroundThread.getExecutor(), mListener); final String property = mDeviceConfig.getProperty(DeviceConfig.NAMESPACE_DISPLAY_MANAGER, KEY_HIGH_REFRESH_RATE_BLACKLIST); - updateBlacklist(property); + updateDenylist(property); } - private void updateBlacklist(@Nullable String property) { + private void updateDenylist(@Nullable String property) { synchronized (mLock) { - mBlacklistedPackages.clear(); + mDenylistedPackages.clear(); if (property != null) { String[] packages = property.split(","); for (String pkg : packages) { String pkgName = pkg.trim(); if (!pkgName.isEmpty()) { - mBlacklistedPackages.add(pkgName); + mDenylistedPackages.add(pkgName); } } } else { // If there's no config, or the config has been deleted, fallback to the device's // default denylist - for (String pkg : mDefaultBlacklist) { - mBlacklistedPackages.add(pkg); + for (String pkg : mDefaultDenylist) { + mDenylistedPackages.add(pkg); } } } } - boolean isBlacklisted(String packageName) { + boolean isDenylisted(String packageName) { synchronized (mLock) { - return mBlacklistedPackages.contains(packageName); + return mDenylistedPackages.contains(packageName); } } void dump(PrintWriter pw) { - pw.println("High Refresh Rate Blacklist"); + pw.println("High Refresh Rate Denylist"); pw.println(" Packages:"); synchronized (mLock) { - for (String pkg : mBlacklistedPackages) { + for (String pkg : mDenylistedPackages) { pw.println(" " + pkg); } } @@ -100,13 +100,13 @@ class HighRefreshRateBlacklist { void dispose() { mDeviceConfig.removeOnPropertiesChangedListener(mListener); mDeviceConfig = null; - mBlacklistedPackages.clear(); + mDenylistedPackages.clear(); } private class OnPropertiesChangedListener implements DeviceConfig.OnPropertiesChangedListener { public void onPropertiesChanged(@NonNull DeviceConfig.Properties properties) { if (properties.getKeyset().contains(KEY_HIGH_REFRESH_RATE_BLACKLIST)) { - updateBlacklist( + updateDenylist( properties.getString(KEY_HIGH_REFRESH_RATE_BLACKLIST, null /*default*/)); } } diff --git a/services/core/java/com/android/server/wm/RefreshRatePolicy.java b/services/core/java/com/android/server/wm/RefreshRatePolicy.java index 072116f04aac..91014aa69831 100644 --- a/services/core/java/com/android/server/wm/RefreshRatePolicy.java +++ b/services/core/java/com/android/server/wm/RefreshRatePolicy.java @@ -30,7 +30,7 @@ class RefreshRatePolicy { private final int mLowRefreshRateId; private final ArraySet<String> mNonHighRefreshRatePackages = new ArraySet<>(); - private final HighRefreshRateBlacklist mHighRefreshRateBlacklist; + private final HighRefreshRateDenylist mHighRefreshRateDenylist; private final WindowManagerService mWmService; /** @@ -55,9 +55,9 @@ class RefreshRatePolicy { static final int LAYER_PRIORITY_NOT_FOCUSED_WITH_MODE = 2; RefreshRatePolicy(WindowManagerService wmService, DisplayInfo displayInfo, - HighRefreshRateBlacklist blacklist) { + HighRefreshRateDenylist denylist) { mLowRefreshRateId = findLowRefreshRateModeId(displayInfo); - mHighRefreshRateBlacklist = blacklist; + mHighRefreshRateDenylist = denylist; mWmService = wmService; } @@ -108,7 +108,7 @@ class RefreshRatePolicy { } // If app is denylisted using higher refresh rate, return default (lower) refresh rate - if (mHighRefreshRateBlacklist.isBlacklisted(packageName)) { + if (mHighRefreshRateDenylist.isDenylisted(packageName)) { return mLowRefreshRateId; } return 0; diff --git a/services/core/java/com/android/server/wm/SafeActivityOptions.java b/services/core/java/com/android/server/wm/SafeActivityOptions.java index ede6708d5f8f..9205401ed2ee 100644 --- a/services/core/java/com/android/server/wm/SafeActivityOptions.java +++ b/services/core/java/com/android/server/wm/SafeActivityOptions.java @@ -150,6 +150,14 @@ public class SafeActivityOptions { } /** + * Gets the original options passed in. It should only be used for logging. DO NOT use it as a + * condition in the logic of activity launch. + */ + ActivityOptions getOriginalOptions() { + return mOriginalOptions; + } + + /** * @see ActivityOptions#popAppVerificationBundle */ Bundle popAppVerificationBundle() { diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java index ae8f7a556ffd..2b93080a8dad 100644 --- a/services/core/java/com/android/server/wm/WindowContainer.java +++ b/services/core/java/com/android/server/wm/WindowContainer.java @@ -32,6 +32,7 @@ import static android.view.SurfaceControl.Transaction; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS_ANIM; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ORIENTATION; +import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_SYNC_ENGINE; import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER; import static com.android.server.wm.AppTransition.MAX_APP_TRANSITION_DURATION; import static com.android.server.wm.IdentifierProto.HASH_CODE; @@ -2886,6 +2887,8 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< // If we are invisible, no need to sync, likewise if we are already engaged in a sync, // we can't support overlapping syncs on a single container yet. if (!isVisible() || mWaitingListener != null) { + ProtoLog.v(WM_DEBUG_SYNC_ENGINE, "- NOT adding to sync: visible=%b " + + "hasListener=%b", isVisible(), mWaitingListener != null); return false; } mUsingBLASTSyncTransaction = true; diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 8c2619d75201..a680213c52a7 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -1005,7 +1005,7 @@ public class WindowManagerService extends IWindowManager.Stub final Configuration mTempConfiguration = new Configuration(); - final HighRefreshRateBlacklist mHighRefreshRateBlacklist; + final HighRefreshRateDenylist mHighRefreshRateDenylist; // If true, only the core apps and services are being launched because the device // is in a special boot mode, such as being encrypted or waiting for a decryption password. @@ -1302,7 +1302,7 @@ public class WindowManagerService extends IWindowManager.Stub this, mInputManager, mActivityTaskManager, mH.getLooper()); mDragDropController = new DragDropController(this, mH.getLooper()); - mHighRefreshRateBlacklist = HighRefreshRateBlacklist.create(context.getResources()); + mHighRefreshRateDenylist = HighRefreshRateDenylist.create(context.getResources()); mConstants = new WindowManagerConstants(this, DeviceConfigInterface.REAL); mConstants.start(new HandlerExecutor(mH)); @@ -5939,7 +5939,7 @@ public class WindowManagerService extends IWindowManager.Stub private void dumpHighRefreshRateBlacklist(PrintWriter pw) { pw.println("WINDOW MANAGER HIGH REFRESH RATE BLACKLIST (dumpsys window refresh)"); - mHighRefreshRateBlacklist.dump(pw); + mHighRefreshRateDenylist.dump(pw); } private void dumpTraceStatus(PrintWriter pw) { diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java index 67d230aae0fb..59f209aca6ba 100644 --- a/services/core/java/com/android/server/wm/WindowProcessController.java +++ b/services/core/java/com/android/server/wm/WindowProcessController.java @@ -41,7 +41,6 @@ import static com.android.server.wm.Task.ActivityState.RESUMED; import static com.android.server.wm.Task.ActivityState.STARTED; import static com.android.server.wm.Task.ActivityState.STOPPING; - import android.Manifest; import android.annotation.NonNull; import android.annotation.Nullable; @@ -465,8 +464,10 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio * Allows background activity starts using token {@code entity}. Optionally, you can provide * {@code originatingToken} if you have one such originating token, this is useful for tracing * back the grant in the case of the notification token. + * + * If {@code entity} is already added, this method will update its {@code originatingToken}. */ - public void addAllowBackgroundActivityStartsToken(Binder entity, + public void addOrUpdateAllowBackgroundActivityStartsToken(Binder entity, @Nullable IBinder originatingToken) { synchronized (mAtm.mGlobalLock) { mBackgroundActivityStartTokens.put(entity, originatingToken); @@ -475,7 +476,7 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio /** * Removes token {@code entity} that allowed background activity starts added via {@link - * #addAllowBackgroundActivityStartsToken(Binder, IBinder)}. + * #addOrUpdateAllowBackgroundActivityStartsToken(Binder, IBinder)}. */ public void removeAllowBackgroundActivityStartsToken(Binder entity) { synchronized (mAtm.mGlobalLock) { @@ -485,7 +486,7 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio /** * Returns true if background activity starts are allowed by any token added via {@link - * #addAllowBackgroundActivityStartsToken(Binder, IBinder)}. + * #addOrUpdateAllowBackgroundActivityStartsToken(Binder, IBinder)}. */ public boolean areBackgroundActivityStartsAllowedByToken() { synchronized (mAtm.mGlobalLock) { @@ -1575,10 +1576,14 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio pw.print(prefix); pw.print("mVrThreadTid="); pw.println(mVrThreadTid); } if (mBackgroundActivityStartTokens.size() > 0) { - pw.print(prefix); pw.println("Background activity start tokens:"); + pw.print(prefix); + pw.println("Background activity start tokens (token: originating token):"); for (int i = 0; i < mBackgroundActivityStartTokens.size(); i++) { pw.print(prefix); pw.print(" - "); - pw.println(mBackgroundActivityStartTokens.keyAt(i)); + pw.print(mBackgroundActivityStartTokens.keyAt(i)); + pw.print(": "); + pw.println(mBackgroundActivityStartTokens.valueAt(i)); + } } } diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java index 5e814005a5e2..029c158814b3 100644 --- a/services/core/java/com/android/server/wm/WindowStateAnimator.java +++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java @@ -441,10 +441,6 @@ class WindowStateAnimator { return mSurfaceController; } - if ((mWin.mAttrs.privateFlags & PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY) != 0) { - windowType = SurfaceControl.WINDOW_TYPE_DONT_SCREENSHOT; - } - w.setHasSurface(false); if (DEBUG_ANIM) { @@ -462,6 +458,10 @@ class WindowStateAnimator { flags |= SurfaceControl.SECURE; } + if ((mWin.mAttrs.privateFlags & PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY) != 0) { + flags |= SurfaceControl.SKIP_SCREENSHOT; + } + calculateSurfaceBounds(w, attrs, mTmpSize); final int width = mTmpSize.width(); final int height = mTmpSize.height(); diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/CallerIdentity.java b/services/devicepolicy/java/com/android/server/devicepolicy/CallerIdentity.java index 5193fa85d238..b6b4d8a04cb6 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/CallerIdentity.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/CallerIdentity.java @@ -58,4 +58,12 @@ class CallerIdentity { @Nullable public ComponentName getComponentName() { return mComponentName; } + + public boolean hasAdminComponent() { + return mComponentName != null; + } + + public boolean hasPackage() { + return mPackageName != null; + } } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 183a1495b075..8f1fb127ec17 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -1567,6 +1567,54 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } /** + * Creates a new {@link CallerIdentity} object to represent the caller's identity. If no + * component name is provided, look up the component name and fill it in for the caller. + */ + private CallerIdentity getCallerIdentityOptionalAdmin(@Nullable ComponentName adminComponent) { + if (adminComponent == null) { + ActiveAdmin admin = getActiveAdminOfCaller(); + if (admin != null) { + return getCallerIdentity(admin.info.getComponent()); + } + throw new SecurityException("Caller is not an active admin"); + } else { + return getCallerIdentity(adminComponent); + } + } + + /** + * Creates a new {@link CallerIdentity} object to represent the caller's identity. If no + * package name is provided, look up the package name and fill it in for the caller. + */ + private CallerIdentity getCallerIdentityOptionalPackage(@Nullable String callerPackage) { + if (callerPackage == null) { + ActiveAdmin admin = getActiveAdminOfCaller(); + if (admin != null) { + return getCallerIdentity(admin.info.getPackageName()); + } + throw new SecurityException("Caller is not an active admin"); + } else { + return getCallerIdentity(callerPackage); + } + } + + /** + * Retrieves the active admin of the caller. This method should not be called directly and + * should only be called by {@link #getCallerIdentityOptionalAdmin} or + * {@link #getCallerIdentityOptionalPackage}. + */ + private ActiveAdmin getActiveAdminOfCaller() { + final int callerUid = mInjector.binderGetCallingUid(); + final DevicePolicyData policy = getUserData(UserHandle.getUserId(callerUid)); + for (ActiveAdmin admin : policy.mAdminList) { + if (admin.getUid() == callerUid) { + return admin; + } + } + return null; + } + + /** * Checks if the device is in COMP mode, and if so migrates it to managed profile on a * corporate owned device. */ @@ -3071,7 +3119,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId"); - final CallerIdentity caller = getCallerIdentity(adminReceiver); + final CallerIdentity caller = getCallerIdentity(); Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userHandle)); synchronized (getLockObject()) { @@ -3630,6 +3678,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Override public boolean addCrossProfileWidgetProvider(ComponentName admin, String packageName) { + Objects.requireNonNull(admin, "ComponentName is null"); + final CallerIdentity caller = getCallerIdentity(admin); Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller)); List<String> changedProviders = null; @@ -3663,6 +3713,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Override public boolean removeCrossProfileWidgetProvider(ComponentName admin, String packageName) { + Objects.requireNonNull(admin, "ComponentName is null"); + final CallerIdentity caller = getCallerIdentity(admin); Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller)); List<String> changedProviders = null; @@ -3696,6 +3748,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Override public List<String> getCrossProfileWidgetProviders(ComponentName admin) { + Objects.requireNonNull(admin, "ComponentName is null"); + final CallerIdentity caller = getCallerIdentity(admin); Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller)); @@ -4833,8 +4887,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { byte[] cert, byte[] chain, String alias, boolean requestAccess, boolean isUserSelectable) { final CallerIdentity caller = getCallerIdentity(who, callerPackage); - Preconditions.checkCallAuthorization(isDeviceOwner(caller) - || isProfileOwner(caller) || isCallerDelegate(caller, DELEGATION_CERT_INSTALL)); + Preconditions.checkCallAuthorization((caller.hasAdminComponent() + && (isProfileOwner(caller) || isDeviceOwner(caller))) + || (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_CERT_INSTALL))); final long id = mInjector.binderClearCallingIdentity(); try { @@ -4872,8 +4927,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Override public boolean removeKeyPair(ComponentName who, String callerPackage, String alias) { final CallerIdentity caller = getCallerIdentity(who, callerPackage); - Preconditions.checkCallAuthorization(isDeviceOwner(caller) - || isProfileOwner(caller) || isCallerDelegate(caller, DELEGATION_CERT_INSTALL)); + Preconditions.checkCallAuthorization((caller.hasAdminComponent() + && (isProfileOwner(caller) || isDeviceOwner(caller))) + || (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_CERT_INSTALL))); final long id = Binder.clearCallingIdentity(); try { @@ -4908,8 +4964,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { Preconditions.checkStringNotEmpty(packageName, "Package to grant to cannot be empty"); final CallerIdentity caller = getCallerIdentity(who, callerPackage); - Preconditions.checkCallAuthorization(isDeviceOwner(caller) - || isProfileOwner(caller) || isCallerDelegate(caller, DELEGATION_CERT_INSTALL)); + Preconditions.checkCallAuthorization((caller.hasAdminComponent() + && (isProfileOwner(caller) || isDeviceOwner(caller))) + || (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_CERT_INSTALL))); final int granteeUid; try { @@ -5052,8 +5109,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { enforceCallerCanRequestDeviceIdAttestation(caller); enforceIndividualAttestationSupportedIfRequested(attestationUtilsFlags); } else { - Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller) - || isCallerDelegate(caller, DELEGATION_CERT_INSTALL)); + Preconditions.checkCallAuthorization((caller.hasAdminComponent() + && (isProfileOwner(caller) || isDeviceOwner(caller))) + || (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_CERT_INSTALL))); } // As the caller will be granted access to the key, ensure no UID was specified, as @@ -5147,8 +5205,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { public boolean setKeyPairCertificate(ComponentName who, String callerPackage, String alias, byte[] cert, byte[] chain, boolean isUserSelectable) { final CallerIdentity caller = getCallerIdentity(who, callerPackage); - Preconditions.checkCallAuthorization(isDeviceOwner(caller) - || isProfileOwner(caller) || isCallerDelegate(caller, DELEGATION_CERT_INSTALL)); + Preconditions.checkCallAuthorization((caller.hasAdminComponent() + && (isProfileOwner(caller) || isDeviceOwner(caller))) + || (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_CERT_INSTALL))); final long id = mInjector.binderClearCallingIdentity(); try (final KeyChainConnection keyChainConnection = @@ -5613,6 +5672,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { public boolean setAlwaysOnVpnPackage(ComponentName who, String vpnPackage, boolean lockdown, List<String> lockdownWhitelist) throws SecurityException { + Objects.requireNonNull(who, "ComponentName is null"); + enforceProfileOrDeviceOwner(who); final CallerIdentity caller = getCallerIdentity(who); @@ -5752,16 +5813,16 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } final CallerIdentity caller = getCallerIdentity(); boolean calledByProfileOwnerOnOrgOwnedDevice = - isProfileOwnerOfOrganizationOwnedDevice(caller); + isProfileOwnerOfOrganizationOwnedDevice(caller.getUserId()); if (calledOnParentInstance) { Preconditions.checkCallAuthorization(calledByProfileOwnerOnOrgOwnedDevice, "Wiping the entire device can only be done by a profile owner on " + "organization-owned device."); } if ((flags & WIPE_RESET_PROTECTION_DATA) != 0) { - Preconditions.checkCallAuthorization( - isDeviceOwner(caller) || calledByProfileOwnerOnOrgOwnedDevice, - "Only device owners or proflie owners of organization-owned device can set " + Preconditions.checkCallAuthorization(isCallerDeviceOwner(caller.getUid()) + || calledByProfileOwnerOnOrgOwnedDevice, + "Only device owners or profile owners of organization-owned device can set " + "WIPE_RESET_PROTECTION_DATA"); } @@ -5960,9 +6021,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId"); - final CallerIdentity caller = comp != null - ? getCallerIdentity(comp) - : getCallerIdentity(); + final CallerIdentity caller = getCallerIdentityOptionalAdmin(comp); Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userHandle)); Preconditions.checkCallAuthorization(hasCallingOrSelfPermission(BIND_DEVICE_ADMIN)); @@ -6399,9 +6458,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId"); - final CallerIdentity caller = who != null - ? getCallerIdentity(who) - : getCallerIdentity(); + final CallerIdentity caller = getCallerIdentityOptionalAdmin(who); Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userHandle)); synchronized (getLockObject()) { @@ -6435,9 +6492,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId"); - final CallerIdentity caller = callerPackage != null - ? getCallerIdentity(callerPackage) - : getCallerIdentity(); + final CallerIdentity caller = getCallerIdentityOptionalPackage(callerPackage); Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userHandle)); // It's not critical here, but let's make sure the package name is correct, in case @@ -6546,9 +6601,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return false; } if (parent) { - Objects.requireNonNull(who, "ComponentName is null"); Preconditions.checkCallAuthorization( - isProfileOwnerOfOrganizationOwnedDevice(getCallerIdentity(who))); + isProfileOwnerOfOrganizationOwnedDevice(getCallerIdentity().getUserId())); } synchronized (getLockObject()) { @@ -6934,9 +6988,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return false; } if (parent) { - Objects.requireNonNull(who, "ComponentName is null"); Preconditions.checkCallAuthorization( - isProfileOwnerOfOrganizationOwnedDevice(getCallerIdentity(who))); + isProfileOwnerOfOrganizationOwnedDevice(getCallerIdentity().getUserId())); } synchronized (getLockObject()) { @@ -7070,8 +7123,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } Objects.requireNonNull(packageList, "packageList is null"); final CallerIdentity caller = getCallerIdentity(who, callerPackage); - Preconditions.checkCallAuthorization(isDeviceOwner(caller) - || isCallerDelegate(caller, DELEGATION_KEEP_UNINSTALLED_PACKAGES)); + Preconditions.checkCallAuthorization((caller.hasAdminComponent() && isDeviceOwner(caller)) + || (caller.hasPackage() + && isCallerDelegate(caller, DELEGATION_KEEP_UNINSTALLED_PACKAGES))); synchronized (getLockObject()) { // Get the device owner @@ -7097,8 +7151,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return null; } final CallerIdentity caller = getCallerIdentity(who, callerPackage); - Preconditions.checkCallAuthorization(isDeviceOwner(caller) - || isCallerDelegate(caller, DELEGATION_KEEP_UNINSTALLED_PACKAGES)); + Preconditions.checkCallAuthorization((caller.hasAdminComponent() && isDeviceOwner(caller)) + || (caller.hasPackage() + && isCallerDelegate(caller, DELEGATION_KEEP_UNINSTALLED_PACKAGES))); // TODO In split system user mode, allow apps on user 0 to query the list synchronized (getLockObject()) { @@ -8517,8 +8572,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { public void setApplicationRestrictions(ComponentName who, String callerPackage, String packageName, Bundle settings) { final CallerIdentity caller = getCallerIdentity(who, callerPackage); - Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller) - || isCallerDelegate(caller, DELEGATION_APP_RESTRICTIONS)); + Preconditions.checkCallAuthorization((caller.hasAdminComponent() + && (isProfileOwner(caller) || isDeviceOwner(caller))) + || (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_APP_RESTRICTIONS))); mInjector.binderWithCleanCallingIdentity(() -> { mUserManager.setApplicationRestrictions(packageName, settings, @@ -8558,9 +8614,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { Objects.requireNonNull(agent, "agent null"); Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId"); - final CallerIdentity caller = admin != null - ? getCallerIdentity(admin) - : getCallerIdentity(); + final CallerIdentity caller = getCallerIdentityOptionalAdmin(admin); Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userHandle)); synchronized (getLockObject()) { @@ -9442,8 +9496,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { public Bundle getApplicationRestrictions(ComponentName who, String callerPackage, String packageName) { final CallerIdentity caller = getCallerIdentity(who, callerPackage); - Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller) - || isCallerDelegate(caller, DELEGATION_APP_RESTRICTIONS)); + Preconditions.checkCallAuthorization((caller.hasAdminComponent() + && (isProfileOwner(caller) || isDeviceOwner(caller))) + || (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_APP_RESTRICTIONS))); return mInjector.binderWithCleanCallingIdentity(() -> { Bundle bundle = mUserManager.getApplicationRestrictions(packageName, @@ -9458,8 +9513,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { public String[] setPackagesSuspended(ComponentName who, String callerPackage, String[] packageNames, boolean suspended) { final CallerIdentity caller = getCallerIdentity(who, callerPackage); - Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller) - || isCallerDelegate(caller, DELEGATION_PACKAGE_ACCESS)); + Preconditions.checkCallAuthorization((caller.hasAdminComponent() + && (isProfileOwner(caller) || isDeviceOwner(caller))) + || (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_PACKAGE_ACCESS))); String[] result = null; synchronized (getLockObject()) { @@ -9489,8 +9545,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Override public boolean isPackageSuspended(ComponentName who, String callerPackage, String packageName) { final CallerIdentity caller = getCallerIdentity(who, callerPackage); - Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller) - || isCallerDelegate(caller, DELEGATION_PACKAGE_ACCESS)); + Preconditions.checkCallAuthorization((caller.hasAdminComponent() + && (isProfileOwner(caller) || isDeviceOwner(caller))) + || (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_PACKAGE_ACCESS))); synchronized (getLockObject()) { long id = mInjector.binderClearCallingIdentity(); @@ -9650,8 +9707,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { public boolean setApplicationHidden(ComponentName who, String callerPackage, String packageName, boolean hidden, boolean parent) { final CallerIdentity caller = getCallerIdentity(who, callerPackage); - Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller) - || isCallerDelegate(caller, DELEGATION_PACKAGE_ACCESS)); + Preconditions.checkCallAuthorization((caller.hasAdminComponent() + && (isProfileOwner(caller) || isDeviceOwner(caller))) + || (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_PACKAGE_ACCESS))); final int userId = parent ? getProfileParentId(caller.getUserId()) : caller.getUserId(); boolean result; @@ -9682,8 +9740,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { public boolean isApplicationHidden(ComponentName who, String callerPackage, String packageName, boolean parent) { final CallerIdentity caller = getCallerIdentity(who, callerPackage); - Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller) - || isCallerDelegate(caller, DELEGATION_PACKAGE_ACCESS)); + Preconditions.checkCallAuthorization((caller.hasAdminComponent() + && (isProfileOwner(caller) || isDeviceOwner(caller))) + || (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_PACKAGE_ACCESS))); final int userId = parent ? getProfileParentId(caller.getUserId()) : caller.getUserId(); synchronized (getLockObject()) { @@ -9716,8 +9775,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Override public void enableSystemApp(ComponentName who, String callerPackage, String packageName) { final CallerIdentity caller = getCallerIdentity(who, callerPackage); - Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller) - || isCallerDelegate(caller, DELEGATION_ENABLE_SYSTEM_APP)); + Preconditions.checkCallAuthorization((caller.hasAdminComponent() + && (isProfileOwner(caller) || isDeviceOwner(caller))) + || (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_ENABLE_SYSTEM_APP))); synchronized (getLockObject()) { final boolean isDemo = isCurrentUserDemo(); @@ -9759,8 +9819,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Override public int enableSystemAppWithIntent(ComponentName who, String callerPackage, Intent intent) { final CallerIdentity caller = getCallerIdentity(who, callerPackage); - Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller) - || isCallerDelegate(caller, DELEGATION_ENABLE_SYSTEM_APP)); + Preconditions.checkCallAuthorization((caller.hasAdminComponent() + && (isProfileOwner(caller) || isDeviceOwner(caller))) + || (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_ENABLE_SYSTEM_APP))); int numberOfAppsInstalled = 0; synchronized (getLockObject()) { @@ -9827,8 +9888,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { public boolean installExistingPackage(ComponentName who, String callerPackage, String packageName) { final CallerIdentity caller = getCallerIdentity(who, callerPackage); - Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller) - || isCallerDelegate(caller, DELEGATION_INSTALL_EXISTING_PACKAGE)); + Preconditions.checkCallAuthorization((caller.hasAdminComponent() + && (isProfileOwner(caller) || isDeviceOwner(caller))) + || (caller.hasPackage() + && isCallerDelegate(caller, DELEGATION_INSTALL_EXISTING_PACKAGE))); boolean result; synchronized (getLockObject()) { @@ -9941,8 +10004,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { public void setUninstallBlocked(ComponentName who, String callerPackage, String packageName, boolean uninstallBlocked) { final CallerIdentity caller = getCallerIdentity(who, callerPackage); - Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller) - || isCallerDelegate(caller, DELEGATION_BLOCK_UNINSTALL)); + Preconditions.checkCallAuthorization((caller.hasAdminComponent() + && (isProfileOwner(caller) || isDeviceOwner(caller))) + || (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_BLOCK_UNINSTALL))); final int userId = caller.getUserId(); synchronized (getLockObject()) { @@ -10489,6 +10553,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Override public void setLocationEnabled(ComponentName who, boolean locationEnabled) { + Preconditions.checkNotNull(who, "ComponentName is null"); + final CallerIdentity caller = getCallerIdentity(who); Preconditions.checkCallAuthorization(isDeviceOwner(caller)); @@ -11596,8 +11662,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Override public void setPermissionPolicy(ComponentName admin, String callerPackage, int policy) { final CallerIdentity caller = getCallerIdentity(admin, callerPackage); - Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller) - || isCallerDelegate(caller, DELEGATION_PERMISSION_GRANT)); + Preconditions.checkCallAuthorization((caller.hasAdminComponent() + && (isProfileOwner(caller) || isDeviceOwner(caller))) + || (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_PERMISSION_GRANT))); synchronized (getLockObject()) { DevicePolicyData userPolicy = getUserData(caller.getUserId()); @@ -11630,8 +11697,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { Objects.requireNonNull(callback); final CallerIdentity caller = getCallerIdentity(admin, callerPackage); - Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller) - || isCallerDelegate(caller, DELEGATION_PERMISSION_GRANT)); + Preconditions.checkCallAuthorization((caller.hasAdminComponent() + && (isProfileOwner(caller) || isDeviceOwner(caller))) + || (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_PERMISSION_GRANT))); synchronized (getLockObject()) { long ident = mInjector.binderClearCallingIdentity(); @@ -11694,9 +11762,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { public int getPermissionGrantState(ComponentName admin, String callerPackage, String packageName, String permission) throws RemoteException { final CallerIdentity caller = getCallerIdentity(admin, callerPackage); - Preconditions.checkCallAuthorization( - isSystemUid(caller) || isDeviceOwner(caller) || isProfileOwner(caller) - || isCallerDelegate(caller, DELEGATION_PERMISSION_GRANT)); + Preconditions.checkCallAuthorization(isSystemUid(caller) || (caller.hasAdminComponent() + && (isProfileOwner(caller) || isDeviceOwner(caller))) + || (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_PERMISSION_GRANT))); synchronized (getLockObject()) { return mInjector.binderWithCleanCallingIdentity(() -> { @@ -13149,8 +13217,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return; } final CallerIdentity caller = getCallerIdentity(admin, packageName); - Preconditions.checkCallAuthorization(isDeviceOwner(caller) - || isCallerDelegate(caller, DELEGATION_NETWORK_LOGGING)); + Preconditions.checkCallAuthorization((caller.hasAdminComponent() && isDeviceOwner(caller)) + || (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_NETWORK_LOGGING))); synchronized (getLockObject()) { if (enabled == isNetworkLoggingEnabledInternalLocked()) { @@ -13267,9 +13335,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return false; } final CallerIdentity caller = getCallerIdentity(admin, packageName); - Preconditions.checkCallAuthorization( - isDeviceOwner(caller) || isCallerDelegate(caller, DELEGATION_NETWORK_LOGGING) - || hasCallingOrSelfPermission(permission.MANAGE_USERS)); + Preconditions.checkCallAuthorization((caller.hasAdminComponent() && isDeviceOwner(caller)) + || (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_NETWORK_LOGGING)) + || hasCallingOrSelfPermission(permission.MANAGE_USERS)); synchronized (getLockObject()) { return isNetworkLoggingEnabledInternalLocked(); @@ -13295,8 +13363,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return null; } final CallerIdentity caller = getCallerIdentity(admin, packageName); - Preconditions.checkCallAuthorization(isDeviceOwner(caller) - || isCallerDelegate(caller, DELEGATION_NETWORK_LOGGING)); + Preconditions.checkCallAuthorization((caller.hasAdminComponent() && isDeviceOwner(caller)) + || (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_NETWORK_LOGGING))); Preconditions.checkCallAuthorization(areAllUsersAffiliatedWithDeviceLocked()); synchronized (getLockObject()) { @@ -14494,6 +14562,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Override public List<String> getUserControlDisabledPackages(ComponentName who) { + Objects.requireNonNull(who, "ComponentName is null"); + final CallerIdentity caller = getCallerIdentity(who); Preconditions.checkCallAuthorization(isDeviceOwner(caller)); diff --git a/services/incremental/IncrementalService.cpp b/services/incremental/IncrementalService.cpp index 44a07a17824f..e8bf468f032e 100644 --- a/services/incremental/IncrementalService.cpp +++ b/services/incremental/IncrementalService.cpp @@ -308,6 +308,7 @@ IncrementalService::~IncrementalService() { } mJobCondition.notify_all(); mJobProcessor.join(); + mLooper->wake(); mCmdLooperThread.join(); mTimedQueue->stop(); mProgressUpdateJobQueue->stop(); @@ -1386,7 +1387,7 @@ bool IncrementalService::mountExistingImage(std::string_view root) { } void IncrementalService::runCmdLooper() { - constexpr auto kTimeoutMsecs = 1000; + constexpr auto kTimeoutMsecs = -1; while (mRunning.load(std::memory_order_relaxed)) { mLooper->pollAll(kTimeoutMsecs); } diff --git a/services/tests/servicestests/src/com/android/server/am/BatteryStatsServiceTest.java b/services/tests/servicestests/src/com/android/server/am/BatteryStatsServiceTest.java new file mode 100644 index 000000000000..488e5cdf33b9 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/am/BatteryStatsServiceTest.java @@ -0,0 +1,122 @@ +/* + * 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.server.am; + +import static org.junit.Assert.assertTrue; + +import android.content.Context; +import android.os.Handler; +import android.os.HandlerThread; +import android.os.Process; + +import androidx.test.InstrumentationRegistry; +import androidx.test.runner.AndroidJUnit4; + +import com.android.internal.os.BatteryStatsImpl; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; + +@RunWith(AndroidJUnit4.class) +public final class BatteryStatsServiceTest { + + private BatteryStatsService mBatteryStatsService; + private HandlerThread mBgThread; + + @Before + public void setUp() { + final Context context = InstrumentationRegistry.getContext(); + mBgThread = new HandlerThread("bg thread"); + mBgThread.start(); + mBatteryStatsService = new BatteryStatsService(context, + context.getCacheDir(), new Handler(mBgThread.getLooper())); + } + + @After + public void tearDown() { + mBatteryStatsService.shutdown(); + mBgThread.quitSafely(); + } + + @Test + public void testAwaitCompletion() throws Exception { + final CountDownLatch readyLatch = new CountDownLatch(2); + final CountDownLatch startLatch = new CountDownLatch(1); + final CountDownLatch testLatch = new CountDownLatch(1); + final AtomicBoolean quiting = new AtomicBoolean(false); + final AtomicBoolean finished = new AtomicBoolean(false); + final int uid = Process.myUid(); + final Thread noteThread = new Thread(() -> { + final int maxIterations = 1000; + final int eventCode = 12345; + final String eventName = "placeholder"; + final BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics(); + + readyLatch.countDown(); + try { + startLatch.await(); + } catch (InterruptedException e) { + } + + for (int i = 0; i < maxIterations && !quiting.get(); i++) { + synchronized (stats) { + mBatteryStatsService.noteEvent(eventCode, eventName, uid); + } + } + finished.set(true); + }); + final Thread waitThread = new Thread(() -> { + readyLatch.countDown(); + try { + startLatch.await(); + } catch (InterruptedException e) { + } + + do { + mBatteryStatsService.takeUidSnapshot(uid); + } while (!finished.get() && !quiting.get()); + + if (!quiting.get()) { + // do one more to ensure we've cleared the queue + mBatteryStatsService.takeUidSnapshot(uid); + } + + testLatch.countDown(); + }); + noteThread.start(); + waitThread.start(); + readyLatch.await(); + startLatch.countDown(); + + try { + assertTrue("Timed out in waiting for the completion of battery event handling", + testLatch.await(10 * 1000, TimeUnit.MILLISECONDS)); + } finally { + quiting.set(true); + noteThread.interrupt(); + noteThread.join(1000); + waitThread.interrupt(); + waitThread.join(1000); + } + } +} diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecControllerTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecControllerTest.java index dd4c0814a441..c60d5fb95846 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecControllerTest.java @@ -95,14 +95,14 @@ public class HdmiCecControllerTest { /** Tests for {@link HdmiCecController#allocateLogicalAddress} */ @Test - public void testAllocatLogicalAddress_TvDevicePreferredNotOcupied() { + public void testAllocateLogicalAddress_TvDevicePreferredNotOccupied() { mHdmiCecController.allocateLogicalAddress(DEVICE_TV, ADDR_TV, mCallback); mTestLooper.dispatchAll(); assertEquals(ADDR_TV, mLogicalAddress); } @Test - public void testAllocatLogicalAddress_TvDeviceNonPreferredNotOcupied() { + public void testAllocateLogicalAddress_TvDeviceNonPreferredNotOccupied() { mHdmiCecController.allocateLogicalAddress(DEVICE_TV, ADDR_UNREGISTERED, mCallback); mTestLooper.dispatchAll(); @@ -110,7 +110,7 @@ public class HdmiCecControllerTest { } @Test - public void testAllocatLogicalAddress_TvDeviceNonPreferredFirstOcupied() { + public void testAllocateLogicalAddress_TvDeviceNonPreferredFirstOccupied() { mNativeWrapper.setPollAddressResponse(ADDR_TV, SendMessageResult.SUCCESS); mHdmiCecController.allocateLogicalAddress(DEVICE_TV, ADDR_UNREGISTERED, mCallback); mTestLooper.dispatchAll(); @@ -118,7 +118,7 @@ public class HdmiCecControllerTest { } @Test - public void testAllocatLogicalAddress_TvDeviceNonPreferredAllOcupied() { + public void testAllocateLogicalAddress_TvDeviceNonPreferredAllOccupied() { mNativeWrapper.setPollAddressResponse(ADDR_TV, SendMessageResult.SUCCESS); mNativeWrapper.setPollAddressResponse(ADDR_SPECIFIC_USE, SendMessageResult.SUCCESS); mHdmiCecController.allocateLogicalAddress(DEVICE_TV, ADDR_UNREGISTERED, mCallback); @@ -127,7 +127,7 @@ public class HdmiCecControllerTest { } @Test - public void testAllocatLogicalAddress_AudioSystemNonPreferredNotOcupied() { + public void testAllocateLogicalAddress_AudioSystemNonPreferredNotOccupied() { mHdmiCecController.allocateLogicalAddress( DEVICE_AUDIO_SYSTEM, ADDR_UNREGISTERED, mCallback); mTestLooper.dispatchAll(); @@ -135,7 +135,7 @@ public class HdmiCecControllerTest { } @Test - public void testAllocatLogicalAddress_AudioSystemNonPreferredAllOcupied() { + public void testAllocateLogicalAddress_AudioSystemNonPreferredAllOccupied() { mNativeWrapper.setPollAddressResponse(ADDR_AUDIO_SYSTEM, SendMessageResult.SUCCESS); mHdmiCecController.allocateLogicalAddress( DEVICE_AUDIO_SYSTEM, ADDR_UNREGISTERED, mCallback); @@ -144,14 +144,14 @@ public class HdmiCecControllerTest { } @Test - public void testAllocatLogicalAddress_PlaybackPreferredNotOccupied() { + public void testAllocateLogicalAddress_PlaybackPreferredNotOccupied() { mHdmiCecController.allocateLogicalAddress(DEVICE_PLAYBACK, ADDR_PLAYBACK_1, mCallback); mTestLooper.dispatchAll(); assertEquals(ADDR_PLAYBACK_1, mLogicalAddress); } @Test - public void testAllocatLogicalAddress_PlaybackPreferredOcuppied() { + public void testAllocateLogicalAddress_PlaybackPreferredOccupied() { mNativeWrapper.setPollAddressResponse(ADDR_PLAYBACK_1, SendMessageResult.SUCCESS); mHdmiCecController.allocateLogicalAddress(DEVICE_PLAYBACK, ADDR_PLAYBACK_1, mCallback); mTestLooper.dispatchAll(); @@ -159,14 +159,14 @@ public class HdmiCecControllerTest { } @Test - public void testAllocatLogicalAddress_PlaybackNoPreferredNotOcuppied() { + public void testAllocateLogicalAddress_PlaybackNoPreferredNotOccupied() { mHdmiCecController.allocateLogicalAddress(DEVICE_PLAYBACK, ADDR_UNREGISTERED, mCallback); mTestLooper.dispatchAll(); assertEquals(ADDR_PLAYBACK_1, mLogicalAddress); } @Test - public void testAllocatLogicalAddress_PlaybackNoPreferredFirstOcuppied() { + public void testAllocateLogicalAddress_PlaybackNoPreferredFirstOccupied() { mNativeWrapper.setPollAddressResponse(ADDR_PLAYBACK_1, SendMessageResult.SUCCESS); mHdmiCecController.allocateLogicalAddress(DEVICE_PLAYBACK, ADDR_UNREGISTERED, mCallback); mTestLooper.dispatchAll(); @@ -174,7 +174,7 @@ public class HdmiCecControllerTest { } @Test - public void testAllocatLogicalAddress_PlaybackNonPreferredFirstTwoOcuppied() { + public void testAllocateLogicalAddress_PlaybackNonPreferredFirstTwoOccupied() { mNativeWrapper.setPollAddressResponse(ADDR_PLAYBACK_1, SendMessageResult.SUCCESS); mNativeWrapper.setPollAddressResponse(ADDR_PLAYBACK_2, SendMessageResult.SUCCESS); mHdmiCecController.allocateLogicalAddress(DEVICE_PLAYBACK, ADDR_UNREGISTERED, mCallback); @@ -183,7 +183,7 @@ public class HdmiCecControllerTest { } @Test - public void testAllocatLogicalAddress_PlaybackNonPreferredAllOcupied() { + public void testAllocateLogicalAddress_PlaybackNonPreferredAllOccupied() { mNativeWrapper.setPollAddressResponse(ADDR_PLAYBACK_1, SendMessageResult.SUCCESS); mNativeWrapper.setPollAddressResponse(ADDR_PLAYBACK_2, SendMessageResult.SUCCESS); mNativeWrapper.setPollAddressResponse(ADDR_PLAYBACK_3, SendMessageResult.SUCCESS); diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java index dac05424e01f..bc747832e253 100644 --- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java +++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java @@ -62,6 +62,7 @@ import com.android.permission.persistence.RuntimePermissionsPersistence; import com.android.server.LocalServices; import com.android.server.pm.parsing.pkg.PackageImpl; import com.android.server.pm.parsing.pkg.ParsedPackage; +import com.android.server.pm.permission.LegacyPermissionDataProvider; import com.android.server.pm.permission.PermissionSettings; import com.google.common.truth.Truth; @@ -94,6 +95,8 @@ public class PackageManagerSettingsTests { PermissionSettings mPermissionSettings; @Mock RuntimePermissionsPersistence mRuntimePermissionsPersistence; + @Mock + LegacyPermissionDataProvider mPermissionDataProvider; @Before public void initializeMocks() { @@ -115,7 +118,7 @@ public class PackageManagerSettingsTests { final Context context = InstrumentationRegistry.getContext(); final Object lock = new Object(); Settings settings = new Settings(context.getFilesDir(), mPermissionSettings, - mRuntimePermissionsPersistence, lock); + mRuntimePermissionsPersistence, mPermissionDataProvider, lock); assertThat(settings.readLPw(createFakeUsers()), is(true)); verifyKeySetMetaData(settings); } @@ -129,7 +132,7 @@ public class PackageManagerSettingsTests { final Context context = InstrumentationRegistry.getContext(); final Object lock = new Object(); Settings settings = new Settings(context.getFilesDir(), mPermissionSettings, - mRuntimePermissionsPersistence, lock); + mRuntimePermissionsPersistence, mPermissionDataProvider, lock); assertThat(settings.readLPw(createFakeUsers()), is(true)); // write out, read back in and verify the same @@ -145,7 +148,7 @@ public class PackageManagerSettingsTests { final Context context = InstrumentationRegistry.getContext(); final Object lock = new Object(); Settings settings = new Settings(context.getFilesDir(), mPermissionSettings, - mRuntimePermissionsPersistence, lock); + mRuntimePermissionsPersistence, mPermissionDataProvider, lock); assertThat(settings.readLPw(createFakeUsers()), is(true)); assertThat(settings.getPackageLPr(PACKAGE_NAME_3), is(notNullValue())); assertThat(settings.getPackageLPr(PACKAGE_NAME_1), is(notNullValue())); @@ -167,13 +170,13 @@ public class PackageManagerSettingsTests { final Context context = InstrumentationRegistry.getContext(); final Object lock = new Object(); Settings settings = new Settings(context.getFilesDir(), mPermissionSettings, - mRuntimePermissionsPersistence, lock); + mRuntimePermissionsPersistence, mPermissionDataProvider, lock); assertThat(settings.readLPw(createFakeUsers()), is(true)); settings.writeLPr(); // Create Settings again to make it read from the new files settings = new Settings(context.getFilesDir(), mPermissionSettings, - mRuntimePermissionsPersistence, lock); + mRuntimePermissionsPersistence, mPermissionDataProvider, lock); assertThat(settings.readLPw(createFakeUsers()), is(true)); PackageSetting ps = settings.getPackageLPr(PACKAGE_NAME_2); @@ -196,7 +199,8 @@ public class PackageManagerSettingsTests { writePackageRestrictions_noSuspendingPackageXml(0); final Object lock = new Object(); final Context context = InstrumentationRegistry.getTargetContext(); - final Settings settingsUnderTest = new Settings(context.getFilesDir(), null, null, lock); + final Settings settingsUnderTest = new Settings(context.getFilesDir(), null, null, null, + lock); settingsUnderTest.mPackages.put(PACKAGE_NAME_1, createPackageSetting(PACKAGE_NAME_1)); settingsUnderTest.mPackages.put(PACKAGE_NAME_2, createPackageSetting(PACKAGE_NAME_2)); settingsUnderTest.readPackageRestrictionsLPr(0); @@ -219,7 +223,8 @@ public class PackageManagerSettingsTests { writePackageRestrictions_noSuspendParamsMapXml(0); final Object lock = new Object(); final Context context = InstrumentationRegistry.getTargetContext(); - final Settings settingsUnderTest = new Settings(context.getFilesDir(), null, null, lock); + final Settings settingsUnderTest = new Settings(context.getFilesDir(), null, null, null, + lock); settingsUnderTest.mPackages.put(PACKAGE_NAME_1, createPackageSetting(PACKAGE_NAME_1)); settingsUnderTest.readPackageRestrictionsLPr(0); @@ -246,7 +251,7 @@ public class PackageManagerSettingsTests { @Test public void testReadWritePackageRestrictions_suspendInfo() { final Context context = InstrumentationRegistry.getTargetContext(); - final Settings settingsUnderTest = new Settings(context.getFilesDir(), null, null, + final Settings settingsUnderTest = new Settings(context.getFilesDir(), null, null, null, new Object()); final PackageSetting ps1 = createPackageSetting(PACKAGE_NAME_1); final PackageSetting ps2 = createPackageSetting(PACKAGE_NAME_2); @@ -344,7 +349,7 @@ public class PackageManagerSettingsTests { @Test public void testReadWritePackageRestrictions_distractionFlags() { final Context context = InstrumentationRegistry.getTargetContext(); - final Settings settingsUnderTest = new Settings(context.getFilesDir(), null, null, + final Settings settingsUnderTest = new Settings(context.getFilesDir(), null, null, null, new Object()); final PackageSetting ps1 = createPackageSetting(PACKAGE_NAME_1); final PackageSetting ps2 = createPackageSetting(PACKAGE_NAME_2); @@ -389,7 +394,7 @@ public class PackageManagerSettingsTests { final Context context = InstrumentationRegistry.getTargetContext(); final Object lock = new Object(); final Settings settingsUnderTest = new Settings(context.getFilesDir(), mPermissionSettings, - mRuntimePermissionsPersistence, lock); + mRuntimePermissionsPersistence, mPermissionDataProvider, lock); final PackageSetting ps1 = createPackageSetting(PACKAGE_NAME_1); ps1.appId = Process.FIRST_APPLICATION_UID; ps1.pkg = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME_1).hideAsParsed()) @@ -465,7 +470,7 @@ public class PackageManagerSettingsTests { final Context context = InstrumentationRegistry.getContext(); final Object lock = new Object(); Settings settings = new Settings(context.getFilesDir(), mPermissionSettings, - mRuntimePermissionsPersistence, lock); + mRuntimePermissionsPersistence, mPermissionDataProvider, lock); assertThat(settings.readLPw(createFakeUsers()), is(true)); // Enable/Disable a package @@ -638,7 +643,7 @@ public class PackageManagerSettingsTests { final Context context = InstrumentationRegistry.getContext(); final Object lock = new Object(); final Settings testSettings01 = new Settings(context.getFilesDir(), mPermissionSettings, - mRuntimePermissionsPersistence, lock); + mRuntimePermissionsPersistence, mPermissionDataProvider, lock); final SharedUserSetting testUserSetting01 = createSharedUserSetting( testSettings01, "TestUser", 10064, 0 /*pkgFlags*/, 0 /*pkgPrivateFlags*/); final PackageSetting testPkgSetting01 = @@ -748,7 +753,7 @@ public class PackageManagerSettingsTests { final Context context = InstrumentationRegistry.getContext(); final Object lock = new Object(); final Settings testSettings01 = new Settings(context.getFilesDir(), mPermissionSettings, - mRuntimePermissionsPersistence, lock); + mRuntimePermissionsPersistence, mPermissionDataProvider, lock); final SharedUserSetting testUserSetting01 = createSharedUserSetting( testSettings01, "TestUser", 10064, 0 /*pkgFlags*/, 0 /*pkgPrivateFlags*/); final PackageSetting testPkgSetting01 = Settings.createNewSetting( diff --git a/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImplTest.java b/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImplTest.java index ebcf10dd019f..509eb2563376 100644 --- a/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImplTest.java +++ b/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImplTest.java @@ -59,6 +59,7 @@ import android.os.HidlMemoryUtil; import android.os.HwParcel; import android.os.IHwBinder; import android.os.IHwInterface; +import android.os.ParcelFileDescriptor; import android.os.RemoteException; import android.os.SharedMemory; import android.system.ErrnoException; @@ -126,7 +127,7 @@ public class SoundTriggerMiddlewareImplTest { model.uuid = "12345678-2345-3456-4567-abcdef987654"; model.vendorUuid = "87654321-5432-6543-7654-456789fedcba"; byte[] data = new byte[]{91, 92, 93, 94, 95}; - model.data = byteArrayToFileDescriptor(data); + model.data = new ParcelFileDescriptor(byteArrayToFileDescriptor(data)); model.dataSize = data.length; return model; } diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java index 9e7226e7cacf..1a4b119a6c99 100755 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -69,6 +69,7 @@ import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.any; import static org.mockito.Mockito.anyInt; +import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doNothing; @@ -102,10 +103,12 @@ import android.app.StatsManager; import android.app.admin.DevicePolicyManagerInternal; import android.app.usage.UsageStatsManagerInternal; import android.companion.ICompanionDeviceManager; +import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.ContentUris; import android.content.Context; import android.content.Intent; +import android.content.IntentFilter; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.IPackageManager; @@ -282,6 +285,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { NotificationHistoryManager mHistoryManager; @Mock StatsManager mStatsManager; + BroadcastReceiver mPackageIntentReceiver; NotificationRecordLoggerFake mNotificationRecordLogger = new NotificationRecordLoggerFake(); private InstanceIdSequence mNotificationInstanceIdSequence = new InstanceIdSequenceFake( 1 << 30); @@ -480,6 +484,28 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mShortcutHelper.setLauncherApps(mLauncherApps); mShortcutHelper.setShortcutServiceInternal(mShortcutServiceInternal); + // Capture PackageIntentReceiver + ArgumentCaptor<BroadcastReceiver> broadcastReceiverCaptor = + ArgumentCaptor.forClass(BroadcastReceiver.class); + ArgumentCaptor<IntentFilter> intentFilterCaptor = + ArgumentCaptor.forClass(IntentFilter.class); + + verify(mContext, atLeastOnce()).registerReceiverAsUser(broadcastReceiverCaptor.capture(), + any(), intentFilterCaptor.capture(), any(), any()); + List<BroadcastReceiver> broadcastReceivers = broadcastReceiverCaptor.getAllValues(); + List<IntentFilter> intentFilters = intentFilterCaptor.getAllValues(); + + for (int i = 0; i < intentFilters.size(); i++) { + final IntentFilter filter = intentFilters.get(i); + if (filter.hasAction(Intent.ACTION_DISTRACTING_PACKAGES_CHANGED) + && filter.hasAction(Intent.ACTION_PACKAGES_UNSUSPENDED) + && filter.hasAction(Intent.ACTION_PACKAGES_SUSPENDED)) { + mPackageIntentReceiver = broadcastReceivers.get(i); + break; + } + } + assertNotNull("package intent receiver should exist", mPackageIntentReceiver); + // Pretend the shortcut exists List<ShortcutInfo> shortcutInfos = new ArrayList<>(); ShortcutInfo info = mock(ShortcutInfo.class); @@ -526,7 +552,13 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { public void tearDown() throws Exception { if (mFile != null) mFile.delete(); clearDeviceConfig(); - mService.unregisterDeviceConfigChange(); + + try { + mService.onDestroy(); + } catch (IllegalStateException e) { + // can throw if a broadcast receiver was never registered + } + InstrumentationRegistry.getInstrumentation() .getUiAutomation().dropShellPermissionIdentity(); } @@ -2533,10 +2565,11 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { @Test public void testHasCompanionDevice_noService() { - mService = new TestableNotificationManagerService(mContext, mNotificationRecordLogger, + NotificationManagerService noManService = + new TestableNotificationManagerService(mContext, mNotificationRecordLogger, mNotificationInstanceIdSequence); - assertFalse(mService.hasCompanionDevice(mListener)); + assertFalse(noManService.hasCompanionDevice(mListener)); } @Test @@ -4347,13 +4380,13 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mService.addNotification(notif2); // on broadcast, hide the 2 notifications - mService.simulatePackageSuspendBroadcast(true, PKG); + simulatePackageSuspendBroadcast(true, PKG, notif1.getUid()); ArgumentCaptor<List> captorHide = ArgumentCaptor.forClass(List.class); verify(mListeners, times(1)).notifyHiddenLocked(captorHide.capture()); assertEquals(2, captorHide.getValue().size()); // on broadcast, unhide the 2 notifications - mService.simulatePackageSuspendBroadcast(false, PKG); + simulatePackageSuspendBroadcast(false, PKG, notif1.getUid()); ArgumentCaptor<List> captorUnhide = ArgumentCaptor.forClass(List.class); verify(mListeners, times(1)).notifyUnhiddenLocked(captorUnhide.capture()); assertEquals(2, captorUnhide.getValue().size()); @@ -4370,7 +4403,24 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mService.addNotification(notif2); // on broadcast, nothing is hidden since no notifications are of package "test_package" - mService.simulatePackageSuspendBroadcast(true, "test_package"); + simulatePackageSuspendBroadcast(true, "test_package", notif1.getUid()); + ArgumentCaptor<List> captor = ArgumentCaptor.forClass(List.class); + verify(mListeners, times(1)).notifyHiddenLocked(captor.capture()); + assertEquals(0, captor.getValue().size()); + } + + @Test + public void testNotificationFromDifferentUserHidden() { + // post 2 notification from this package + final NotificationRecord notif1 = generateNotificationRecord( + mTestNotificationChannel, 1, null, true); + final NotificationRecord notif2 = generateNotificationRecord( + mTestNotificationChannel, 2, null, false); + mService.addNotification(notif1); + mService.addNotification(notif2); + + // on broadcast, nothing is hidden since no notifications are of user 10 with package PKG + simulatePackageSuspendBroadcast(true, PKG, 10); ArgumentCaptor<List> captor = ArgumentCaptor.forClass(List.class); verify(mListeners, times(1)).notifyHiddenLocked(captor.capture()); assertEquals(0, captor.getValue().size()); @@ -4387,16 +4437,18 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mService.addNotification(pkgB); // on broadcast, hide one of the packages - mService.simulatePackageDistractionBroadcast( - PackageManager.RESTRICTION_HIDE_NOTIFICATIONS, new String[] {"a"}); + simulatePackageDistractionBroadcast( + PackageManager.RESTRICTION_HIDE_NOTIFICATIONS, new String[] {"a"}, + new int[] {1000}); ArgumentCaptor<List<NotificationRecord>> captorHide = ArgumentCaptor.forClass(List.class); verify(mListeners, times(1)).notifyHiddenLocked(captorHide.capture()); assertEquals(1, captorHide.getValue().size()); assertEquals("a", captorHide.getValue().get(0).getSbn().getPackageName()); // on broadcast, unhide the package - mService.simulatePackageDistractionBroadcast( - PackageManager.RESTRICTION_HIDE_FROM_SUGGESTIONS, new String[] {"a"}); + simulatePackageDistractionBroadcast( + PackageManager.RESTRICTION_HIDE_FROM_SUGGESTIONS, new String[] {"a"}, + new int[] {1000}); ArgumentCaptor<List<NotificationRecord>> captorUnhide = ArgumentCaptor.forClass(List.class); verify(mListeners, times(1)).notifyUnhiddenLocked(captorUnhide.capture()); assertEquals(1, captorUnhide.getValue().size()); @@ -4414,8 +4466,9 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mService.addNotification(pkgB); // on broadcast, hide one of the packages - mService.simulatePackageDistractionBroadcast( - PackageManager.RESTRICTION_HIDE_NOTIFICATIONS, new String[] {"a", "b"}); + simulatePackageDistractionBroadcast( + PackageManager.RESTRICTION_HIDE_NOTIFICATIONS, new String[] {"a", "b"}, + new int[] {1000, 1001}); ArgumentCaptor<List<NotificationRecord>> captorHide = ArgumentCaptor.forClass(List.class); // should be called only once. @@ -4425,8 +4478,9 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { assertEquals("b", captorHide.getValue().get(1).getSbn().getPackageName()); // on broadcast, unhide the package - mService.simulatePackageDistractionBroadcast( - PackageManager.RESTRICTION_HIDE_FROM_SUGGESTIONS, new String[] {"a", "b"}); + simulatePackageDistractionBroadcast( + PackageManager.RESTRICTION_HIDE_FROM_SUGGESTIONS, new String[] {"a", "b"}, + new int[] {1000, 1001}); ArgumentCaptor<List<NotificationRecord>> captorUnhide = ArgumentCaptor.forClass(List.class); // should be called only once. @@ -4444,8 +4498,9 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mService.addNotification(notif1); // on broadcast, nothing is hidden since no notifications are of package "test_package" - mService.simulatePackageDistractionBroadcast( - PackageManager.RESTRICTION_HIDE_NOTIFICATIONS, new String[] {"test_package"}); + simulatePackageDistractionBroadcast( + PackageManager.RESTRICTION_HIDE_NOTIFICATIONS, new String[] {"test_package"}, + new int[]{notif1.getUid()}); ArgumentCaptor<List> captor = ArgumentCaptor.forClass(List.class); verify(mListeners, times(1)).notifyHiddenLocked(captor.capture()); assertEquals(0, captor.getValue().size()); @@ -7011,4 +7066,34 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { assertTrue(mService.isVisibleToListener(sbn, info)); } + private void simulatePackageSuspendBroadcast(boolean suspend, String pkg, + int uid) { + // mimics receive broadcast that package is (un)suspended + // but does not actually (un)suspend the package + final Bundle extras = new Bundle(); + extras.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST, + new String[]{pkg}); + extras.putIntArray(Intent.EXTRA_CHANGED_UID_LIST, new int[]{uid}); + + final String action = suspend ? Intent.ACTION_PACKAGES_SUSPENDED + : Intent.ACTION_PACKAGES_UNSUSPENDED; + final Intent intent = new Intent(action); + intent.putExtras(extras); + + mPackageIntentReceiver.onReceive(getContext(), intent); + } + + private void simulatePackageDistractionBroadcast(int flag, String[] pkgs, int[] uids) { + // mimics receive broadcast that package is (un)distracting + // but does not actually register that info with packagemanager + final Bundle extras = new Bundle(); + extras.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST, pkgs); + extras.putInt(Intent.EXTRA_DISTRACTION_RESTRICTIONS, flag); + extras.putIntArray(Intent.EXTRA_CHANGED_UID_LIST, uids); + + final Intent intent = new Intent(Intent.ACTION_DISTRACTING_PACKAGES_CHANGED); + intent.putExtras(extras); + + mPackageIntentReceiver.onReceive(getContext(), intent); + } } diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java index 076047b35604..e47881917b2c 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java @@ -63,6 +63,7 @@ import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.notNull; import android.app.ActivityOptions; import android.app.IApplicationThread; @@ -670,7 +671,7 @@ public class ActivityStarterTests extends WindowTestsBase { doReturn(callerIsRecents).when(recentTasks).isCallerRecents(callingUid); // caller is temp allowed if (callerIsTempAllowed) { - callerApp.addAllowBackgroundActivityStartsToken(new Binder(), null); + callerApp.addOrUpdateAllowBackgroundActivityStartsToken(new Binder(), null); } // caller is instrumenting with background activity starts privileges callerApp.setInstrumenting(callerIsInstrumentingWithBackgroundActivityStartPrivileges, @@ -800,6 +801,7 @@ public class ActivityStarterTests extends WindowTestsBase { final Task topTask = new TaskBuilder(mSupervisor).setParentTask(topStack).build(); new ActivityBuilder(mAtm).setTask(topTask).build(); + doReturn(mActivityMetricsLogger).when(mSupervisor).getActivityMetricsLogger(); // Start activity with the same intent as {@code singleTaskActivity} on secondary display. final ActivityOptions options = ActivityOptions.makeBasic() .setLaunchDisplayId(secondaryDisplay.mDisplayId); @@ -813,6 +815,9 @@ public class ActivityStarterTests extends WindowTestsBase { // Ensure secondary display only creates two stacks. verify(secondaryTaskContainer, times(2)).createStack(anyInt(), anyInt(), anyBoolean()); + // The metrics logger should receive the same result and non-null options. + verify(mActivityMetricsLogger).notifyActivityLaunched(any() /* launchingState */, + eq(result), eq(singleTaskActivity), notNull() /* options */); } @Test diff --git a/services/tests/wmtests/src/com/android/server/wm/HighRefreshRateBlacklistTest.java b/services/tests/wmtests/src/com/android/server/wm/HighRefreshRateDenylistTest.java index f53894ad9ec5..c3e1922a09cc 100644 --- a/services/tests/wmtests/src/com/android/server/wm/HighRefreshRateBlacklistTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/HighRefreshRateDenylistTest.java @@ -40,107 +40,107 @@ import java.util.concurrent.Executor; /** * Build/Install/Run: - * atest WmTests:HighRefreshRateBlacklistTest + * atest WmTests:HighRefreshRateDenylistTest */ @SmallTest @Presubmit -public class HighRefreshRateBlacklistTest { +public class HighRefreshRateDenylistTest { private static final String APP1 = "com.android.sample1"; private static final String APP2 = "com.android.sample2"; private static final String APP3 = "com.android.sample3"; - private HighRefreshRateBlacklist mBlacklist; + private HighRefreshRateDenylist mDenylist; @After public void tearDown() { - mBlacklist.dispose(); + mDenylist.dispose(); } @Test - public void testDefaultBlacklist() { + public void testDefaultDenylist() { final Resources r = createResources(APP1, APP2); - mBlacklist = new HighRefreshRateBlacklist(r, new FakeDeviceConfig()); + mDenylist = new HighRefreshRateDenylist(r, new FakeDeviceConfig()); - assertTrue(mBlacklist.isBlacklisted(APP1)); - assertTrue(mBlacklist.isBlacklisted(APP2)); - assertFalse(mBlacklist.isBlacklisted(APP3)); + assertTrue(mDenylist.isDenylisted(APP1)); + assertTrue(mDenylist.isDenylisted(APP2)); + assertFalse(mDenylist.isDenylisted(APP3)); } @Test - public void testNoDefaultBlacklist() { + public void testNoDefaultDenylist() { final Resources r = createResources(); - mBlacklist = new HighRefreshRateBlacklist(r, new FakeDeviceConfig()); + mDenylist = new HighRefreshRateDenylist(r, new FakeDeviceConfig()); - assertFalse(mBlacklist.isBlacklisted(APP1)); + assertFalse(mDenylist.isDenylisted(APP1)); } @Test - public void testDefaultBlacklistIsOverriddenByDeviceConfig() { + public void testDefaultDenylistIsOverriddenByDeviceConfig() { final Resources r = createResources(APP1); final FakeDeviceConfig config = new FakeDeviceConfig(); - config.setBlacklist(APP2 + "," + APP3); - mBlacklist = new HighRefreshRateBlacklist(r, config); + config.setDenylist(APP2 + "," + APP3); + mDenylist = new HighRefreshRateDenylist(r, config); - assertFalse(mBlacklist.isBlacklisted(APP1)); - assertTrue(mBlacklist.isBlacklisted(APP2)); - assertTrue(mBlacklist.isBlacklisted(APP3)); + assertFalse(mDenylist.isDenylisted(APP1)); + assertTrue(mDenylist.isDenylisted(APP2)); + assertTrue(mDenylist.isDenylisted(APP3)); } @Test - public void testDefaultBlacklistIsOverriddenByEmptyDeviceConfig() { + public void testDefaultDenylistIsOverriddenByEmptyDeviceConfig() { final Resources r = createResources(APP1); final FakeDeviceConfig config = new FakeDeviceConfig(); - config.setBlacklist(""); - mBlacklist = new HighRefreshRateBlacklist(r, config); + config.setDenylist(""); + mDenylist = new HighRefreshRateDenylist(r, config); - assertFalse(mBlacklist.isBlacklisted(APP1)); + assertFalse(mDenylist.isDenylisted(APP1)); } @Test - public void testDefaultBlacklistIsOverriddenByDeviceConfigUpdate() { + public void testDefaultDenylistIsOverriddenByDeviceConfigUpdate() { final Resources r = createResources(APP1); final FakeDeviceConfig config = new FakeDeviceConfig(); - mBlacklist = new HighRefreshRateBlacklist(r, config); + mDenylist = new HighRefreshRateDenylist(r, config); // First check that the default denylist is in effect - assertTrue(mBlacklist.isBlacklisted(APP1)); - assertFalse(mBlacklist.isBlacklisted(APP2)); - assertFalse(mBlacklist.isBlacklisted(APP3)); + assertTrue(mDenylist.isDenylisted(APP1)); + assertFalse(mDenylist.isDenylisted(APP2)); + assertFalse(mDenylist.isDenylisted(APP3)); // Then confirm that the DeviceConfig list has propagated and taken effect. - config.setBlacklist(APP2 + "," + APP3); - assertFalse(mBlacklist.isBlacklisted(APP1)); - assertTrue(mBlacklist.isBlacklisted(APP2)); - assertTrue(mBlacklist.isBlacklisted(APP3)); + config.setDenylist(APP2 + "," + APP3); + assertFalse(mDenylist.isDenylisted(APP1)); + assertTrue(mDenylist.isDenylisted(APP2)); + assertTrue(mDenylist.isDenylisted(APP3)); // Finally make sure we go back to the default list if the DeviceConfig gets deleted. - config.setBlacklist(null); - assertTrue(mBlacklist.isBlacklisted(APP1)); - assertFalse(mBlacklist.isBlacklisted(APP2)); - assertFalse(mBlacklist.isBlacklisted(APP3)); + config.setDenylist(null); + assertTrue(mDenylist.isDenylisted(APP1)); + assertFalse(mDenylist.isDenylisted(APP2)); + assertFalse(mDenylist.isDenylisted(APP3)); } @Test public void testOverriddenByDeviceConfigUnrelatedFlagChanged() { final Resources r = createResources(APP1); final FakeDeviceConfig config = new FakeDeviceConfig(); - mBlacklist = new HighRefreshRateBlacklist(r, config); - config.setBlacklist(APP2 + "," + APP3); - assertFalse(mBlacklist.isBlacklisted(APP1)); - assertTrue(mBlacklist.isBlacklisted(APP2)); - assertTrue(mBlacklist.isBlacklisted(APP3)); + mDenylist = new HighRefreshRateDenylist(r, config); + config.setDenylist(APP2 + "," + APP3); + assertFalse(mDenylist.isDenylisted(APP1)); + assertTrue(mDenylist.isDenylisted(APP2)); + assertTrue(mDenylist.isDenylisted(APP3)); // Change an unrelated flag in our namespace and verify that the denylist is intact config.putPropertyAndNotify(DeviceConfig.NAMESPACE_DISPLAY_MANAGER, "someKey", "someValue"); - assertFalse(mBlacklist.isBlacklisted(APP1)); - assertTrue(mBlacklist.isBlacklisted(APP2)); - assertTrue(mBlacklist.isBlacklisted(APP3)); + assertFalse(mDenylist.isDenylisted(APP1)); + assertTrue(mDenylist.isDenylisted(APP2)); + assertTrue(mDenylist.isDenylisted(APP3)); } - private Resources createResources(String... defaultBlacklist) { + private Resources createResources(String... defaultDenylist) { Resources r = mock(Resources.class); when(r.getStringArray(R.array.config_highRefreshRateBlacklist)) - .thenReturn(defaultBlacklist); + .thenReturn(defaultDenylist); return r; } @@ -160,9 +160,9 @@ public class HighRefreshRateBlacklistTest { super.addOnPropertiesChangedListener(namespace, executor, listener); } - void setBlacklist(String blacklist) { + void setDenylist(String denylist) { putPropertyAndNotify(DeviceConfig.NAMESPACE_DISPLAY_MANAGER, - KEY_HIGH_REFRESH_RATE_BLACKLIST, blacklist); + KEY_HIGH_REFRESH_RATE_BLACKLIST, denylist); } } } diff --git a/services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java index e887be0c48c2..77a4b0507a42 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java @@ -48,7 +48,7 @@ public class RefreshRatePolicyTest extends WindowTestsBase { private static final int LOW_MODE_ID = 3; private RefreshRatePolicy mPolicy; - private HighRefreshRateBlacklist mBlacklist = mock(HighRefreshRateBlacklist.class); + private HighRefreshRateDenylist mDenylist = mock(HighRefreshRateDenylist.class); @Before public void setUp() { @@ -61,7 +61,7 @@ public class RefreshRatePolicyTest extends WindowTestsBase { defaultMode.getPhysicalWidth(), defaultMode.getPhysicalHeight(), 60), }; di.defaultModeId = 1; - mPolicy = new RefreshRatePolicy(mWm, di, mBlacklist); + mPolicy = new RefreshRatePolicy(mWm, di, mDenylist); } @Test @@ -81,7 +81,7 @@ public class RefreshRatePolicyTest extends WindowTestsBase { final WindowState blacklistedWindow = createWindow(null, TYPE_BASE_APPLICATION, "blacklistedWindow"); blacklistedWindow.mAttrs.packageName = "com.android.test"; - when(mBlacklist.isBlacklisted("com.android.test")).thenReturn(true); + when(mDenylist.isDenylisted("com.android.test")).thenReturn(true); assertEquals(LOW_MODE_ID, mPolicy.getPreferredModeId(blacklistedWindow)); } @@ -90,7 +90,7 @@ public class RefreshRatePolicyTest extends WindowTestsBase { final WindowState overrideWindow = createWindow(null, TYPE_BASE_APPLICATION, "overrideWindow"); overrideWindow.mAttrs.preferredDisplayModeId = LOW_MODE_ID; - when(mBlacklist.isBlacklisted("com.android.test")).thenReturn(true); + when(mDenylist.isDenylisted("com.android.test")).thenReturn(true); assertEquals(LOW_MODE_ID, mPolicy.getPreferredModeId(overrideWindow)); } diff --git a/services/tests/wmtests/src/com/android/server/wm/ScreenDecorWindowTests.java b/services/tests/wmtests/src/com/android/server/wm/ScreenDecorWindowTests.java index ef74861e9422..25ba6db38e05 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ScreenDecorWindowTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ScreenDecorWindowTests.java @@ -68,6 +68,7 @@ import com.android.compatibility.common.util.SystemUtil; import org.junit.After; import org.junit.Before; +import org.junit.Ignore; import org.junit.Test; import java.util.ArrayList; @@ -142,6 +143,9 @@ public class ScreenDecorWindowTests { assertInsetGreaterOrEqual(mTestActivity, RIGHT, mDecorThickness); } + // Decor windows (i.e windows using PRIVATE_FLAG_IS_SCREEN_DECOR) are no longer supported. + // PRIVATE_FLAG_IS_SCREEN_DECOR and related code will be deprecated/removed soon. + @Ignore @Test public void testMultipleDecors() { // Test 2 decor windows on-top. diff --git a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java index da3319acca24..4d88a4f7db16 100644 --- a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java +++ b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java @@ -337,7 +337,7 @@ public class SystemServicesTestRule implements TestRule { // HighRefreshRateBlacklist with DeviceConfig. We need to undo that here to avoid // leaking mWmService. mWmService.mConstants.dispose(); - mWmService.mHighRefreshRateBlacklist.dispose(); + mWmService.mHighRefreshRateDenylist.dispose(); // This makes sure the posted messages without delay are processed, e.g. // DisplayPolicy#release, WindowManagerService#setAnimationScale. diff --git a/tools/validatekeymaps/Main.cpp b/tools/validatekeymaps/Main.cpp index 7c150f9c8db9..0af6266d1642 100644 --- a/tools/validatekeymaps/Main.cpp +++ b/tools/validatekeymaps/Main.cpp @@ -115,13 +115,13 @@ static bool validateFile(const char* filename) { } case FILETYPE_INPUTDEVICECONFIGURATION: { - PropertyMap* map; - status_t status = PropertyMap::load(String8(filename), &map); - if (status) { - error("Error %d parsing input device configuration file.\n\n", status); + android::base::Result<std::unique_ptr<PropertyMap>> propertyMap = + PropertyMap::load(String8(filename)); + if (!propertyMap.ok()) { + error("Error %d parsing input device configuration file.\n\n", + propertyMap.error().code()); return false; } - delete map; break; } diff --git a/wifi/api/current.txt b/wifi/api/current.txt index 3f5c673eeb81..1c297e7058dd 100644 --- a/wifi/api/current.txt +++ b/wifi/api/current.txt @@ -798,9 +798,15 @@ package android.net.wifi.hotspot2.pps { method public int describeContents(); method public String getFqdn(); method public String getFriendlyName(); + method @Nullable public long[] getMatchAllOis(); + method @Nullable public long[] getMatchAnyOis(); + method @Nullable public String[] getOtherHomePartners(); method public long[] getRoamingConsortiumOis(); method public void setFqdn(String); method public void setFriendlyName(String); + method public void setMatchAllOis(@Nullable long[]); + method public void setMatchAnyOis(@Nullable long[]); + method public void setOtherHomePartners(@Nullable String[]); method public void setRoamingConsortiumOis(long[]); method public void writeToParcel(android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.hotspot2.pps.HomeSp> CREATOR; diff --git a/wifi/api/system-current.txt b/wifi/api/system-current.txt index aab4a2df7633..fd45ebec80ef 100644 --- a/wifi/api/system-current.txt +++ b/wifi/api/system-current.txt @@ -508,7 +508,7 @@ package android.net.wifi { field public static final int EASY_CONNECT_NETWORK_ROLE_AP = 1; // 0x1 field public static final int EASY_CONNECT_NETWORK_ROLE_STA = 0; // 0x0 field public static final String EXTRA_CHANGE_REASON = "changeReason"; - field public static final String EXTRA_LINK_PROPERTIES = "android.net.wifi.extra.LINK_PROPERTIES"; + field @Deprecated public static final String EXTRA_LINK_PROPERTIES = "android.net.wifi.extra.LINK_PROPERTIES"; field public static final String EXTRA_MULTIPLE_NETWORKS_CHANGED = "multipleChanges"; field public static final String EXTRA_OSU_NETWORK = "android.net.wifi.extra.OSU_NETWORK"; field public static final String EXTRA_PREVIOUS_WIFI_AP_STATE = "previous_wifi_state"; diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java index c76f4a63a777..2219bfcd6aab 100644 --- a/wifi/java/android/net/wifi/WifiManager.java +++ b/wifi/java/android/net/wifi/WifiManager.java @@ -1053,9 +1053,6 @@ public class WifiManager { /** * Broadcast intent action indicating that the link configuration changed on wifi. - * <br />Included Extras: - * <br />{@link #EXTRA_LINK_PROPERTIES}: {@link android.net.LinkProperties} object associated - * with the Wi-Fi network. * <br /> No permissions are required to listen to this broadcast. * @hide */ @@ -1071,8 +1068,12 @@ public class WifiManager { * Included in the {@link #ACTION_LINK_CONFIGURATION_CHANGED} broadcast. * * Retrieve with {@link android.content.Intent#getParcelableExtra(String)}. + * + * @deprecated this extra is no longer populated. + * * @hide */ + @Deprecated @SystemApi public static final String EXTRA_LINK_PROPERTIES = "android.net.wifi.extra.LINK_PROPERTIES"; diff --git a/wifi/java/android/net/wifi/hotspot2/pps/HomeSp.java b/wifi/java/android/net/wifi/hotspot2/pps/HomeSp.java index 8f34579f6a5d..35a8ff6095e0 100644 --- a/wifi/java/android/net/wifi/hotspot2/pps/HomeSp.java +++ b/wifi/java/android/net/wifi/hotspot2/pps/HomeSp.java @@ -16,6 +16,7 @@ package android.net.wifi.hotspot2.pps; +import android.annotation.Nullable; import android.os.Parcel; import android.os.Parcelable; import android.text.TextUtils; @@ -139,16 +140,26 @@ public final class HomeSp implements Parcelable { * (MO) tree for more detail. */ private long[] mMatchAllOis = null; + /** - * @hide + * Set a list of HomeOIs such that all OIs in the list must match an OI in the Roaming + * Consortium advertised by a hotspot operator. The list set by this API will have precedence + * over {@link #setMatchAnyOis(long[])}, meaning the list set in {@link #setMatchAnyOis(long[])} + * will only be used for matching if the list set by this API is null or empty. + * + * @param matchAllOis An array of longs containing the HomeOIs */ - public void setMatchAllOis(long[] matchAllOis) { + public void setMatchAllOis(@Nullable long[] matchAllOis) { mMatchAllOis = matchAllOis; } + /** - * @hide + * Get the list of HomeOIs such that all OIs in the list must match an OI in the Roaming + * Consortium advertised by a hotspot operator. + * + * @return An array of longs containing the HomeOIs */ - public long[] getMatchAllOis() { + public @Nullable long[] getMatchAllOis() { return mMatchAllOis; } @@ -159,23 +170,34 @@ public final class HomeSp implements Parcelable { * of that Hotspot provider (e.g. successful authentication with such Hotspot * is possible). * - * {@link #mMatchAllOIs} will have precedence over this one, meaning this list will - * only be used for matching if {@link #mMatchAllOIs} is null or empty. + * The list set by {@link #setMatchAllOis(long[])} will have precedence over this one, meaning + * this list will only be used for matching if the list set by {@link #setMatchAllOis(long[])} + * is null or empty. * * Refer to HomeSP/HomeOIList subtree in PerProviderSubscription (PPS) Management Object * (MO) tree for more detail. */ private long[] mMatchAnyOis = null; + /** - * @hide + * Set a list of HomeOIs such that any OI in the list matches an OI in the Roaming Consortium + * advertised by a hotspot operator. The list set by {@link #setMatchAllOis(long[])} + * will have precedence over this API, meaning this list will only be used for matching if the + * list set by {@link #setMatchAllOis(long[])} is null or empty. + * + * @param matchAnyOis An array of longs containing the HomeOIs */ - public void setMatchAnyOis(long[] matchAnyOis) { + public void setMatchAnyOis(@Nullable long[] matchAnyOis) { mMatchAnyOis = matchAnyOis; } + /** - * @hide + * Get a list of HomeOIs such that any OI in the list matches an OI in the Roaming Consortium + * advertised by a hotspot operator. + * + * @return An array of longs containing the HomeOIs */ - public long[] getMatchAnyOis() { + public @Nullable long[] getMatchAnyOis() { return mMatchAnyOis; } @@ -186,16 +208,25 @@ public final class HomeSp implements Parcelable { * operator merges between the providers. */ private String[] mOtherHomePartners = null; + /** - * @hide + * Set the list of FQDN (Fully Qualified Domain Name) of other Home partner providers. + * + * @param otherHomePartners Array of Strings containing the FQDNs of other Home partner + * providers */ - public void setOtherHomePartners(String[] otherHomePartners) { + public void setOtherHomePartners(@Nullable String[] otherHomePartners) { mOtherHomePartners = otherHomePartners; } + /** - * @hide + * Get the list of FQDN (Fully Qualified Domain Name) of other Home partner providers set in + * the profile. + * + * @return Array of Strings containing the FQDNs of other Home partner providers set in the + * profile */ - public String[] getOtherHomePartners() { + public @Nullable String[] getOtherHomePartners() { return mOtherHomePartners; } |