diff options
74 files changed, 2163 insertions, 583 deletions
diff --git a/api/test-current.txt b/api/test-current.txt index d2dbacb2fb81..fa018a316644 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -2313,6 +2313,7 @@ package android.provider { } public static interface DeviceConfig.WindowManager { + field public static final String KEY_SYSTEM_GESTURES_EXCLUDED_BY_PRE_Q_STICKY_IMMERSIVE = "system_gestures_excluded_by_pre_q_sticky_immersive"; field public static final String KEY_SYSTEM_GESTURE_EXCLUSION_LIMIT_DP = "system_gesture_exclusion_limit_dp"; } diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index 60f1424220f6..fb72e651cebd 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -240,7 +240,8 @@ public class AppOpsManager { public @interface UidState {} /** - * Uid state: The UID is a foreground persistent app. + * Uid state: The UID is a foreground persistent app. The lower the UID + * state the more important the UID is for the user. * @hide */ @TestApi @@ -248,7 +249,8 @@ public class AppOpsManager { public static final int UID_STATE_PERSISTENT = 100; /** - * Uid state: The UID is top foreground app. + * Uid state: The UID is top foreground app. The lower the UID + * state the more important the UID is for the user. * @hide */ @TestApi @@ -257,6 +259,7 @@ public class AppOpsManager { /** * Uid state: The UID is running a foreground service of location type. + * The lower the UID state the more important the UID is for the user. * @hide */ @TestApi @@ -264,7 +267,8 @@ public class AppOpsManager { public static final int UID_STATE_FOREGROUND_SERVICE_LOCATION = 300; /** - * Uid state: The UID is running a foreground service. + * Uid state: The UID is running a foreground service. The lower the UID + * state the more important the UID is for the user. * @hide */ @TestApi @@ -279,7 +283,8 @@ public class AppOpsManager { public static final int UID_STATE_MAX_LAST_NON_RESTRICTED = UID_STATE_FOREGROUND_SERVICE; /** - * Uid state: The UID is a foreground app. + * Uid state: The UID is a foreground app. The lower the UID + * state the more important the UID is for the user. * @hide */ @TestApi @@ -287,7 +292,8 @@ public class AppOpsManager { public static final int UID_STATE_FOREGROUND = 500; /** - * Uid state: The UID is a background app. + * Uid state: The UID is a background app. The lower the UID + * state the more important the UID is for the user. * @hide */ @TestApi @@ -295,7 +301,8 @@ public class AppOpsManager { public static final int UID_STATE_BACKGROUND = 600; /** - * Uid state: The UID is a cached app. + * Uid state: The UID is a cached app. The lower the UID + * state the more important the UID is for the user. * @hide */ @TestApi @@ -2507,7 +2514,7 @@ public class AppOpsManager { } /** - * @return The duration of the operation in milliseconds. + * @return The duration of the operation in milliseconds. The duration is in wall time. */ public long getDuration() { return getLastDuration(MAX_PRIORITY_UID_STATE, MIN_PRIORITY_UID_STATE, OP_FLAGS_ALL); @@ -2515,7 +2522,7 @@ public class AppOpsManager { /** * Return the duration in milliseconds the app accessed this op while - * in the foreground. + * in the foreground. The duration is in wall time. * * @param flags The flags which are any combination of * {@link #OP_FLAG_SELF}, {@link #OP_FLAG_TRUSTED_PROXY}, @@ -2534,7 +2541,7 @@ public class AppOpsManager { /** * Return the duration in milliseconds the app accessed this op while - * in the background. + * in the background. The duration is in wall time. * * @param flags The flags which are any combination of * {@link #OP_FLAG_SELF}, {@link #OP_FLAG_TRUSTED_PROXY}, @@ -2553,7 +2560,7 @@ public class AppOpsManager { /** * Return the duration in milliseconds the app accessed this op for - * a given range of UID states. + * a given range of UID states. The duration is in wall time. * * @param fromUidState The UID state for which to query. Could be one of * {@link #UID_STATE_PERSISTENT}, {@link #UID_STATE_TOP}, @@ -3968,6 +3975,7 @@ public class AppOpsManager { /** * Gets the total duration the app op was accessed (performed) in the foreground. + * The duration is in wall time. * * @param flags The flags which are any combination of * {@link #OP_FLAG_SELF}, {@link #OP_FLAG_TRUSTED_PROXY}, @@ -3986,6 +3994,7 @@ public class AppOpsManager { /** * Gets the total duration the app op was accessed (performed) in the background. + * The duration is in wall time. * * @param flags The flags which are any combination of * {@link #OP_FLAG_SELF}, {@link #OP_FLAG_TRUSTED_PROXY}, @@ -4004,7 +4013,7 @@ public class AppOpsManager { /** * Gets the total duration the app op was accessed (performed) for a given - * range of UID states. + * range of UID states. The duration is in wall time. * * @param fromUidState The UID state from which to query. Could be one of * {@link #UID_STATE_PERSISTENT}, {@link #UID_STATE_TOP}, diff --git a/core/java/android/app/servertransaction/NewIntentItem.java b/core/java/android/app/servertransaction/NewIntentItem.java index 2d1883836d02..bb775fc4a5fb 100644 --- a/core/java/android/app/servertransaction/NewIntentItem.java +++ b/core/java/android/app/servertransaction/NewIntentItem.java @@ -17,6 +17,7 @@ package android.app.servertransaction; import static android.app.servertransaction.ActivityLifecycleItem.ON_RESUME; +import static android.app.servertransaction.ActivityLifecycleItem.UNDEFINED; import android.annotation.UnsupportedAppUsage; import android.app.ClientTransactionHandler; @@ -38,10 +39,11 @@ public class NewIntentItem extends ClientTransactionItem { @UnsupportedAppUsage private List<ReferrerIntent> mIntents; + private boolean mResume; @Override public int getPostExecutionState() { - return ON_RESUME; + return mResume ? ON_RESUME : UNDEFINED; } @Override @@ -58,12 +60,13 @@ public class NewIntentItem extends ClientTransactionItem { private NewIntentItem() {} /** Obtain an instance initialized with provided params. */ - public static NewIntentItem obtain(List<ReferrerIntent> intents) { + public static NewIntentItem obtain(List<ReferrerIntent> intents, boolean resume) { NewIntentItem instance = ObjectPool.obtain(NewIntentItem.class); if (instance == null) { instance = new NewIntentItem(); } instance.mIntents = intents; + instance.mResume = resume; return instance; } @@ -71,6 +74,7 @@ public class NewIntentItem extends ClientTransactionItem { @Override public void recycle() { mIntents = null; + mResume = false; ObjectPool.recycle(this); } @@ -80,11 +84,13 @@ public class NewIntentItem extends ClientTransactionItem { /** Write to Parcel. */ @Override public void writeToParcel(Parcel dest, int flags) { + dest.writeBoolean(mResume); dest.writeTypedList(mIntents, flags); } /** Read from Parcel. */ private NewIntentItem(Parcel in) { + mResume = in.readBoolean(); mIntents = in.createTypedArrayList(ReferrerIntent.CREATOR); } @@ -108,18 +114,19 @@ public class NewIntentItem extends ClientTransactionItem { return false; } final NewIntentItem other = (NewIntentItem) o; - return Objects.equals(mIntents, other.mIntents); + return mResume == other.mResume && Objects.equals(mIntents, other.mIntents); } @Override public int hashCode() { int result = 17; + result = 31 * result + (mResume ? 1 : 0); result = 31 * result + mIntents.hashCode(); return result; } @Override public String toString() { - return "NewIntentItem{intents=" + mIntents + "}"; + return "NewIntentItem{intents=" + mIntents + ",resume=" + mResume + "}"; } } diff --git a/core/java/android/content/pm/RegisteredServicesCache.java b/core/java/android/content/pm/RegisteredServicesCache.java index b0b1874107ce..23fbefb73c50 100644 --- a/core/java/android/content/pm/RegisteredServicesCache.java +++ b/core/java/android/content/pm/RegisteredServicesCache.java @@ -17,7 +17,6 @@ package android.content.pm; import android.Manifest; -import android.annotation.Nullable; import android.annotation.UnsupportedAppUsage; import android.content.BroadcastReceiver; import android.content.ComponentName; @@ -177,8 +176,7 @@ public abstract class RegisteredServicesCache<V> { mContext.registerReceiver(mUserRemovedReceiver, userFilter); } - @VisibleForTesting - protected void handlePackageEvent(Intent intent, int userId) { + private void handlePackageEvent(Intent intent, int userId) { // Don't regenerate the services map when the package is removed or its // ASEC container unmounted as a step in replacement. The subsequent // _ADDED / _AVAILABLE call will regenerate the map in the final state. @@ -240,9 +238,6 @@ public abstract class RegisteredServicesCache<V> { public void invalidateCache(int userId) { synchronized (mServicesLock) { - if (DEBUG) { - Slog.d(TAG, "invalidating cache for " + userId + " " + mInterfaceName); - } final UserServices<V> user = findOrCreateUserLocked(userId); user.services = null; onServicesChangedLocked(userId); @@ -472,48 +467,34 @@ public abstract class RegisteredServicesCache<V> { * or null to assume that everything is affected. * @param userId the user for whom to update the services map. */ - private void generateServicesMap(@Nullable int[] changedUids, int userId) { + private void generateServicesMap(int[] changedUids, int userId) { if (DEBUG) { Slog.d(TAG, "generateServicesMap() for " + userId + ", changed UIDs = " + Arrays.toString(changedUids)); } + final ArrayList<ServiceInfo<V>> serviceInfos = new ArrayList<>(); + final List<ResolveInfo> resolveInfos = queryIntentServices(userId); + for (ResolveInfo resolveInfo : resolveInfos) { + try { + ServiceInfo<V> info = parseServiceInfo(resolveInfo); + if (info == null) { + Log.w(TAG, "Unable to load service info " + resolveInfo.toString()); + continue; + } + serviceInfos.add(info); + } catch (XmlPullParserException | IOException e) { + Log.w(TAG, "Unable to load service info " + resolveInfo.toString(), e); + } + } + synchronized (mServicesLock) { final UserServices<V> user = findOrCreateUserLocked(userId); - final boolean cacheInvalid = user.services == null; - if (cacheInvalid) { + final boolean firstScan = user.services == null; + if (firstScan) { user.services = Maps.newHashMap(); } - final ArrayList<ServiceInfo<V>> serviceInfos = new ArrayList<>(); - final List<ResolveInfo> resolveInfos = queryIntentServices(userId); - - for (ResolveInfo resolveInfo : resolveInfos) { - try { - // when changedUids == null, we want to do a rescan of everything, this means - // it's the initial scan, and containsUid will trivially return true - // when changedUids != null, we got here because a package changed, but - // invalidateCache could have been called (thus user.services == null), and we - // should query from PackageManager again - if (!cacheInvalid - && !containsUid( - changedUids, resolveInfo.serviceInfo.applicationInfo.uid)) { - if (DEBUG) { - Slog.d(TAG, "Skipping parseServiceInfo for " + resolveInfo); - } - continue; - } - ServiceInfo<V> info = parseServiceInfo(resolveInfo); - if (info == null) { - Log.w(TAG, "Unable to load service info " + resolveInfo.toString()); - continue; - } - serviceInfos.add(info); - } catch (XmlPullParserException | IOException e) { - Log.w(TAG, "Unable to load service info " + resolveInfo.toString(), e); - } - } - StringBuilder changes = new StringBuilder(); boolean changed = false; for (ServiceInfo<V> info : serviceInfos) { @@ -534,7 +515,7 @@ public abstract class RegisteredServicesCache<V> { changed = true; user.services.put(info.type, info); user.persistentServices.put(info.type, info.uid); - if (!(user.mPersistentServicesFileDidNotExist && cacheInvalid)) { + if (!(user.mPersistentServicesFileDidNotExist && firstScan)) { notifyListener(info.type, userId, false /* removed */); } } else if (previousUid == info.uid) { diff --git a/core/java/android/os/RedactingFileDescriptor.java b/core/java/android/os/RedactingFileDescriptor.java index 4e5eaac3442f..a1ed2146b38e 100644 --- a/core/java/android/os/RedactingFileDescriptor.java +++ b/core/java/android/os/RedactingFileDescriptor.java @@ -35,7 +35,7 @@ import java.util.Arrays; /** * Variant of {@link FileDescriptor} that allows its creator to specify regions - * that should be redacted (appearing as zeros to the reader). + * that should be redacted. * * @hide */ @@ -44,13 +44,16 @@ public class RedactingFileDescriptor { private static final boolean DEBUG = true; private volatile long[] mRedactRanges; + private volatile long[] mFreeOffsets; private FileDescriptor mInner = null; private ParcelFileDescriptor mOuter = null; - private RedactingFileDescriptor(Context context, File file, int mode, long[] redactRanges) + private RedactingFileDescriptor( + Context context, File file, int mode, long[] redactRanges, long[] freeOffsets) throws IOException { mRedactRanges = checkRangesArgument(redactRanges); + mFreeOffsets = freeOffsets; try { try { @@ -88,13 +91,17 @@ public class RedactingFileDescriptor { * * @param file The underlying file to open. * @param mode The {@link ParcelFileDescriptor} mode to open with. - * @param redactRanges List of file offsets that should be redacted, stored + * @param redactRanges List of file ranges that should be redacted, stored * as {@code [start1, end1, start2, end2, ...]}. Start values are * inclusive and end values are exclusive. + * @param freePositions List of file offsets at which the four byte value 'free' should be + * written instead of zeros within parts of the file covered by {@code redactRanges}. + * Non-redacted bytes will not be modified even if covered by a 'free'. This is + * useful for overwriting boxes in ISOBMFF files with padding data. */ public static ParcelFileDescriptor open(Context context, File file, int mode, - long[] redactRanges) throws IOException { - return new RedactingFileDescriptor(context, file, mode, redactRanges).mOuter; + long[] redactRanges, long[] freePositions) throws IOException { + return new RedactingFileDescriptor(context, file, mode, redactRanges, freePositions).mOuter; } /** @@ -169,6 +176,15 @@ public class RedactingFileDescriptor { for (long j = start; j < end; j++) { data[(int) (j - offset)] = 0; } + // Overwrite data at 'free' offsets within the redaction ranges. + for (long freeOffset : mFreeOffsets) { + final long freeEnd = freeOffset + 4; + final long redactFreeStart = Math.max(freeOffset, start); + final long redactFreeEnd = Math.min(freeEnd, end); + for (long j = redactFreeStart; j < redactFreeEnd; j++) { + data[(int) (j - offset)] = (byte) "free".charAt((int) (j - freeOffset)); + } + } } return n; } diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java index 920eb4b51775..e30ba38c127f 100644 --- a/core/java/android/provider/DeviceConfig.java +++ b/core/java/android/provider/DeviceConfig.java @@ -325,6 +325,17 @@ public final class DeviceConfig { */ @TestApi String KEY_SYSTEM_GESTURE_EXCLUSION_LIMIT_DP = "system_gesture_exclusion_limit_dp"; + + /** + * Key for controlling whether system gestures are implicitly excluded by windows requesting + * sticky immersive mode from apps that are targeting an SDK prior to Q. + * + * @see android.provider.DeviceConfig#NAMESPACE_WINDOW_MANAGER + * @hide + */ + @TestApi + String KEY_SYSTEM_GESTURES_EXCLUDED_BY_PRE_Q_STICKY_IMMERSIVE = + "system_gestures_excluded_by_pre_q_sticky_immersive"; } private static final Object sLock = new Object(); diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java index f9d27bb46592..00206fc38d1d 100644 --- a/core/java/com/android/internal/app/ChooserActivity.java +++ b/core/java/com/android/internal/app/ChooserActivity.java @@ -1534,7 +1534,11 @@ public class ChooserActivity extends ResolverActivity { if (driList.get(i).getResolvedComponentName().equals( resultList.get(j).getTargetComponent())) { ShortcutManager.ShareShortcutInfo shareShortcutInfo = resultList.get(j); - ChooserTarget chooserTarget = convertToChooserTarget(shareShortcutInfo); + // Incoming results are ordered but without a score. Create a score + // based on the index in order to be sorted appropriately when joined + // with legacy direct share api results. + float score = Math.max(1.0f - (0.05f * j), 0.0f); + ChooserTarget chooserTarget = convertToChooserTarget(shareShortcutInfo, score); chooserTargets.add(chooserTarget); if (mDirectShareAppTargetCache != null && appTargets != null) { mDirectShareAppTargetCache.put(chooserTarget, appTargets.get(j)); @@ -1580,7 +1584,8 @@ public class ChooserActivity extends ResolverActivity { return false; } - private ChooserTarget convertToChooserTarget(ShortcutManager.ShareShortcutInfo shareShortcut) { + private ChooserTarget convertToChooserTarget(ShortcutManager.ShareShortcutInfo shareShortcut, + float score) { ShortcutInfo shortcutInfo = shareShortcut.getShortcutInfo(); Bundle extras = new Bundle(); extras.putString(Intent.EXTRA_SHORTCUT_ID, shortcutInfo.getId()); @@ -1591,7 +1596,7 @@ public class ChooserActivity extends ResolverActivity { null, // The ranking score for this target (0.0-1.0); the system will omit items with low // scores when there are too many Direct Share items. - 1.0f, + score, // The name of the component to be launched if this target is chosen. shareShortcut.getTargetComponent().clone(), // The extra values here will be merged into the Intent when this target is chosen. diff --git a/core/java/com/android/internal/app/PlatLogoActivity.java b/core/java/com/android/internal/app/PlatLogoActivity.java index 57785443919e..157e0a74712b 100644 --- a/core/java/com/android/internal/app/PlatLogoActivity.java +++ b/core/java/com/android/internal/app/PlatLogoActivity.java @@ -16,252 +16,167 @@ package com.android.internal.app; +import android.animation.ObjectAnimator; import android.animation.TimeAnimator; import android.app.Activity; import android.content.ActivityNotFoundException; import android.content.ContentResolver; import android.content.Intent; +import android.content.res.ColorStateList; +import android.graphics.Bitmap; +import android.graphics.BitmapShader; import android.graphics.Canvas; -import android.graphics.Color; import android.graphics.ColorFilter; +import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.Path; +import android.graphics.PixelFormat; +import android.graphics.Shader; import android.graphics.drawable.Drawable; import android.os.Bundle; import android.provider.Settings; import android.util.Log; +import android.view.HapticFeedbackConstants; import android.view.MotionEvent; -import android.view.MotionEvent.PointerCoords; import android.view.View; -import android.widget.FrameLayout; +import android.view.ViewGroup; +import android.widget.ImageView; + +import com.android.internal.R; import org.json.JSONObject; +/** + * @hide + */ public class PlatLogoActivity extends Activity { - FrameLayout layout; - TimeAnimator anim; - PBackground bg; - - private class PBackground extends Drawable { - private float maxRadius, radius, x, y, dp; - private int[] palette; - private int darkest; - private float offset; - - public PBackground() { - randomizePalette(); - } - - /** - * set inner radius of "p" logo - */ - public void setRadius(float r) { - this.radius = Math.max(48*dp, r); - } - - /** - * move the "p" - */ - public void setPosition(float x, float y) { - this.x = x; - this.y = y; - } - - /** - * for animating the "p" - */ - public void setOffset(float o) { - this.offset = o; - } - - /** - * rough luminance calculation - * https://www.w3.org/TR/AERT/#color-contrast - */ - public float lum(int rgb) { - return ((Color.red(rgb) * 299f) + (Color.green(rgb) * 587f) + (Color.blue(rgb) * 114f)) / 1000f; - } - - /** - * create a random evenly-spaced color palette - * guaranteed to contrast! - */ - public void randomizePalette() { - final int slots = 2 + (int)(Math.random() * 2); - float[] color = new float[] { (float) Math.random() * 360f, 1f, 1f }; - palette = new int[slots]; - darkest = 0; - for (int i=0; i<slots; i++) { - palette[i] = Color.HSVToColor(color); - color[0] = (color[0] + 360f/slots) % 360f; - if (lum(palette[i]) < lum(palette[darkest])) darkest = i; - } - - final StringBuilder str = new StringBuilder(); - for (int c : palette) { - str.append(String.format("#%08x ", c)); - } - Log.v("PlatLogoActivity", "color palette: " + str); - } - - @Override - public void draw(Canvas canvas) { - if (dp == 0) dp = getResources().getDisplayMetrics().density; - final float width = canvas.getWidth(); - final float height = canvas.getHeight(); - if (radius == 0) { - setPosition(width / 2, height / 2); - setRadius(width / 6); - } - final float inner_w = radius * 0.667f; - - final Paint paint = new Paint(); - paint.setStrokeCap(Paint.Cap.BUTT); - canvas.translate(x, y); - - Path p = new Path(); - p.moveTo(-radius, height); - p.lineTo(-radius, 0); - p.arcTo(-radius, -radius, radius, radius, -180, 270, false); - p.lineTo(-radius, radius); - - float w = Math.max(canvas.getWidth(), canvas.getHeight()) * 1.414f; - paint.setStyle(Paint.Style.FILL); - - int i=0; - while (w > radius*2 + inner_w*2) { - paint.setColor(0xFF000000 | palette[i % palette.length]); - // for a slower but more complete version: - // paint.setStrokeWidth(w); - // canvas.drawPath(p, paint); - canvas.drawOval(-w/2, -w/2, w/2, w/2, paint); - w -= inner_w * (1.1f + Math.sin((i/20f + offset) * 3.14159f)); - i++; - } - - // the innermost circle needs to be a constant color to avoid rapid flashing - paint.setColor(0xFF000000 | palette[(darkest+1) % palette.length]); - canvas.drawOval(-radius, -radius, radius, radius, paint); - - p.reset(); - p.moveTo(-radius, height); - p.lineTo(-radius, 0); - p.arcTo(-radius, -radius, radius, radius, -180, 270, false); - p.lineTo(-radius + inner_w, radius); - - paint.setStyle(Paint.Style.STROKE); - paint.setStrokeWidth(inner_w*2); - paint.setColor(palette[darkest]); - canvas.drawPath(p, paint); - paint.setStrokeWidth(inner_w); - paint.setColor(0xFFFFFFFF); - canvas.drawPath(p, paint); - } - - @Override - public void setAlpha(int alpha) { - - } - - @Override - public void setColorFilter(ColorFilter colorFilter) { - - } + ImageView mZeroView, mOneView; + BackslashDrawable mBackslash; + int mClicks; + + static final Paint sPaint = new Paint(); + static { + sPaint.setStyle(Paint.Style.STROKE); + sPaint.setStrokeWidth(4f); + sPaint.setStrokeCap(Paint.Cap.SQUARE); + } - @Override - public int getOpacity() { - return 0; + @Override + protected void onPause() { + if (mBackslash != null) { + mBackslash.stopAnimating(); } + mClicks = 0; + super.onPause(); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + final float dp = getResources().getDisplayMetrics().density; - layout = new FrameLayout(this); - setContentView(layout); + getWindow().getDecorView().setSystemUiVisibility( + View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_LAYOUT_STABLE); + getWindow().setNavigationBarColor(0); + getWindow().setStatusBarColor(0); - bg = new PBackground(); - layout.setBackground(bg); + getActionBar().hide(); - final ContentResolver cr = getContentResolver(); + setContentView(R.layout.platlogo_layout); - layout.setOnTouchListener(new View.OnTouchListener() { - final String TOUCH_STATS = "touch.stats"; + mBackslash = new BackslashDrawable((int) (50 * dp)); - final PointerCoords pc0 = new PointerCoords(); - final PointerCoords pc1 = new PointerCoords(); + mOneView = findViewById(R.id.one); + mOneView.setImageDrawable(new OneDrawable()); + mZeroView = findViewById(R.id.zero); + mZeroView.setImageDrawable(new ZeroDrawable()); - double pressure_min, pressure_max; - int maxPointers; - int tapCount; + final ViewGroup root = (ViewGroup) mOneView.getParent(); + root.setClipChildren(false); + root.setBackground(mBackslash); + root.getBackground().setAlpha(0x20); + View.OnTouchListener tl = new View.OnTouchListener() { + float mOffsetX, mOffsetY; + long mClickTime; + ObjectAnimator mRotAnim; @Override public boolean onTouch(View v, MotionEvent event) { - final float pressure = event.getPressure(); + measureTouchPressure(event); switch (event.getActionMasked()) { case MotionEvent.ACTION_DOWN: - pressure_min = pressure_max = pressure; - // fall through - case MotionEvent.ACTION_MOVE: - if (pressure < pressure_min) pressure_min = pressure; - if (pressure > pressure_max) pressure_max = pressure; - final int pc = event.getPointerCount(); - if (pc > maxPointers) maxPointers = pc; - if (pc > 1) { - event.getPointerCoords(0, pc0); - event.getPointerCoords(1, pc1); - bg.setRadius((float) Math.hypot(pc0.x - pc1.x, pc0.y - pc1.y) / 2f); + v.animate().scaleX(1.1f).scaleY(1.1f); + v.getParent().bringChildToFront(v); + mOffsetX = event.getRawX() - v.getX(); + mOffsetY = event.getRawY() - v.getY(); + long now = System.currentTimeMillis(); + if (now - mClickTime < 350) { + mRotAnim = ObjectAnimator.ofFloat(v, View.ROTATION, + v.getRotation(), v.getRotation() + 3600); + mRotAnim.setDuration(10000); + mRotAnim.start(); + mClickTime = 0; + } else { + mClickTime = now; } break; - case MotionEvent.ACTION_CANCEL: + case MotionEvent.ACTION_MOVE: + v.setX(event.getRawX() - mOffsetX); + v.setY(event.getRawY() - mOffsetY); + v.performHapticFeedback(HapticFeedbackConstants.TEXT_HANDLE_MOVE); + break; case MotionEvent.ACTION_UP: - try { - final String touchDataJson = Settings.System.getString(cr, TOUCH_STATS); - final JSONObject touchData = new JSONObject( - touchDataJson != null ? touchDataJson : "{}"); - if (touchData.has("min")) { - pressure_min = Math.min(pressure_min, touchData.getDouble("min")); - } - if (touchData.has("max")) { - pressure_max = Math.max(pressure_max, touchData.getDouble("max")); - } - touchData.put("min", pressure_min); - touchData.put("max", pressure_max); - Settings.System.putString(cr, TOUCH_STATS, touchData.toString()); - } catch (Exception e) { - Log.e("PlatLogoActivity", "Can't write touch settings", e); - } - - if (maxPointers == 1) { - tapCount ++; - if (tapCount < 7) { - bg.randomizePalette(); - } else { - launchNextStage(); - } - } else { - tapCount = 0; - } - maxPointers = 0; + v.performClick(); + // fall through + case MotionEvent.ACTION_CANCEL: + v.animate().scaleX(1f).scaleY(1f); + if (mRotAnim != null) mRotAnim.cancel(); + testOverlap(); break; } return true; } - }); + }; + + findViewById(R.id.one).setOnTouchListener(tl); + findViewById(R.id.zero).setOnTouchListener(tl); + findViewById(R.id.text).setOnTouchListener(tl); + } + + private void testOverlap() { + final float width = mZeroView.getWidth(); + final float targetX = mZeroView.getX() + width * .2f; + final float targetY = mZeroView.getY() + width * .3f; + if (Math.hypot(targetX - mOneView.getX(), targetY - mOneView.getY()) < width * .2f + && Math.abs(mOneView.getRotation() % 360 - 315) < 15) { + mOneView.animate().x(mZeroView.getX() + width * .2f); + mOneView.animate().y(mZeroView.getY() + width * .3f); + mOneView.setRotation(mOneView.getRotation() % 360); + mOneView.animate().rotation(315); + mOneView.performHapticFeedback(HapticFeedbackConstants.CONFIRM); + + mBackslash.startAnimating(); + + mClicks++; + if (mClicks >= 7) { + launchNextStage(); + } + } else { + mBackslash.stopAnimating(); + } } private void launchNextStage() { final ContentResolver cr = getContentResolver(); - if (Settings.System.getLong(cr, Settings.System.EGG_MODE, 0) == 0) { + if (Settings.System.getLong(cr, "egg_mode" /* Settings.System.EGG_MODE */, 0) == 0) { // For posterity: the moment this user unlocked the easter egg try { Settings.System.putLong(cr, - Settings.System.EGG_MODE, + "egg_mode", // Settings.System.EGG_MODE, System.currentTimeMillis()); } catch (RuntimeException e) { - Log.e("PlatLogoActivity", "Can't write settings", e); + Log.e("com.android.internal.app.PlatLogoActivity", "Can't write settings", e); } } try { @@ -270,36 +185,206 @@ public class PlatLogoActivity extends Activity { | Intent.FLAG_ACTIVITY_CLEAR_TASK) .addCategory("com.android.internal.category.PLATLOGO")); } catch (ActivityNotFoundException ex) { - Log.e("PlatLogoActivity", "No more eggs."); + Log.e("com.android.internal.app.PlatLogoActivity", "No more eggs."); } finish(); } + static final String TOUCH_STATS = "touch.stats"; + double mPressureMin = 0, mPressureMax = -1; + + private void measureTouchPressure(MotionEvent event) { + final float pressure = event.getPressure(); + switch (event.getActionMasked()) { + case MotionEvent.ACTION_DOWN: + if (mPressureMax < 0) { + mPressureMin = mPressureMax = pressure; + } + break; + case MotionEvent.ACTION_MOVE: + if (pressure < mPressureMin) mPressureMin = pressure; + if (pressure > mPressureMax) mPressureMax = pressure; + break; + } + } + + private void syncTouchPressure() { + try { + final String touchDataJson = Settings.System.getString( + getContentResolver(), TOUCH_STATS); + final JSONObject touchData = new JSONObject( + touchDataJson != null ? touchDataJson : "{}"); + if (touchData.has("min")) { + mPressureMin = Math.min(mPressureMin, touchData.getDouble("min")); + } + if (touchData.has("max")) { + mPressureMax = Math.max(mPressureMax, touchData.getDouble("max")); + } + if (mPressureMax >= 0) { + touchData.put("min", mPressureMin); + touchData.put("max", mPressureMax); + Settings.System.putString(getContentResolver(), TOUCH_STATS, touchData.toString()); + } + } catch (Exception e) { + Log.e("com.android.internal.app.PlatLogoActivity", "Can't write touch settings", e); + } + } + @Override public void onStart() { super.onStart(); + syncTouchPressure(); + } - bg.randomizePalette(); + @Override + public void onStop() { + syncTouchPressure(); + super.onStop(); + } - anim = new TimeAnimator(); - anim.setTimeListener( - new TimeAnimator.TimeListener() { - @Override - public void onTimeUpdate(TimeAnimator animation, long totalTime, long deltaTime) { - bg.setOffset((float) totalTime / 60000f); - bg.invalidateSelf(); - } - }); + static class ZeroDrawable extends Drawable { + int mTintColor; + + @Override + public void draw(Canvas canvas) { + sPaint.setColor(mTintColor | 0xFF000000); + + canvas.save(); + canvas.scale(canvas.getWidth() / 24f, canvas.getHeight() / 24f); + + canvas.drawCircle(12f, 12f, 10f, sPaint); + canvas.restore(); + } - anim.start(); + @Override + public void setAlpha(int alpha) { } + + @Override + public void setColorFilter(ColorFilter colorFilter) { } + + @Override + public void setTintList(ColorStateList tint) { + mTintColor = tint.getDefaultColor(); + } + + @Override + public int getOpacity() { + return PixelFormat.TRANSLUCENT; + } } - @Override - public void onStop() { - if (anim != null) { - anim.cancel(); - anim = null; + static class OneDrawable extends Drawable { + int mTintColor; + + @Override + public void draw(Canvas canvas) { + sPaint.setColor(mTintColor | 0xFF000000); + + canvas.save(); + canvas.scale(canvas.getWidth() / 24f, canvas.getHeight() / 24f); + + final Path p = new Path(); + p.moveTo(12f, 21.83f); + p.rLineTo(0f, -19.67f); + p.rLineTo(-5f, 0f); + canvas.drawPath(p, sPaint); + canvas.restore(); + } + + @Override + public void setAlpha(int alpha) { } + + @Override + public void setColorFilter(ColorFilter colorFilter) { } + + @Override + public void setTintList(ColorStateList tint) { + mTintColor = tint.getDefaultColor(); + } + + @Override + public int getOpacity() { + return PixelFormat.TRANSLUCENT; + } + } + + private static class BackslashDrawable extends Drawable implements TimeAnimator.TimeListener { + Bitmap mTile; + Paint mPaint = new Paint(); + BitmapShader mShader; + TimeAnimator mAnimator = new TimeAnimator(); + Matrix mMatrix = new Matrix(); + + public void draw(Canvas canvas) { + canvas.drawPaint(mPaint); + } + + BackslashDrawable(int width) { + mTile = Bitmap.createBitmap(width, width, Bitmap.Config.ALPHA_8); + mAnimator.setTimeListener(this); + + final Canvas tileCanvas = new Canvas(mTile); + final float w = tileCanvas.getWidth(); + final float h = tileCanvas.getHeight(); + + final Path path = new Path(); + path.moveTo(0, 0); + path.lineTo(w / 2, 0); + path.lineTo(w, h / 2); + path.lineTo(w, h); + path.close(); + + path.moveTo(0, h / 2); + path.lineTo(w / 2, h); + path.lineTo(0, h); + path.close(); + + final Paint slashPaint = new Paint(); + slashPaint.setAntiAlias(true); + slashPaint.setStyle(Paint.Style.FILL); + slashPaint.setColor(0xFF000000); + tileCanvas.drawPath(path, slashPaint); + + //mPaint.setColor(0xFF0000FF); + mShader = new BitmapShader(mTile, Shader.TileMode.REPEAT, Shader.TileMode.REPEAT); + mPaint.setShader(mShader); + } + + public void startAnimating() { + if (!mAnimator.isStarted()) { + mAnimator.start(); + } + } + + public void stopAnimating() { + if (mAnimator.isStarted()) { + mAnimator.cancel(); + } + } + + @Override + public void setAlpha(int alpha) { + mPaint.setAlpha(alpha); + } + + @Override + public void setColorFilter(ColorFilter colorFilter) { + mPaint.setColorFilter(colorFilter); + } + + @Override + public int getOpacity() { + return PixelFormat.TRANSLUCENT; + } + + @Override + public void onTimeUpdate(TimeAnimator animation, long totalTime, long deltaTime) { + if (mShader != null) { + mMatrix.postTranslate(deltaTime / 4f, 0); + mShader.setLocalMatrix(mMatrix); + invalidateSelf(); + } } - super.onStop(); } } + diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java index f905ea2dc6f2..58ce03baa136 100644 --- a/core/java/com/android/internal/app/ResolverActivity.java +++ b/core/java/com/android/internal/app/ResolverActivity.java @@ -889,7 +889,8 @@ public class ResolverActivity extends Activity { : mAdapterView.getCheckedItemPosition(); boolean hasIndexBeenFiltered = !mAdapter.hasFilteredItem(); ResolveInfo ri = mAdapter.resolveInfoForPosition(which, hasIndexBeenFiltered); - if (!ri.handleAllWebDataURI && id == R.id.button_always) { + if (mUseLayoutForBrowsables + && !ri.handleAllWebDataURI && id == R.id.button_always) { showSettingsForSelected(ri); } else { startSelected(which, id == R.id.button_always, hasIndexBeenFiltered); diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java index 6f4f3374ac73..fe66cf9aab7d 100644 --- a/core/java/com/android/internal/policy/DecorView.java +++ b/core/java/com/android/internal/policy/DecorView.java @@ -1311,7 +1311,7 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind return semiTransparentBarColor; } else if ((flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) == 0) { return Color.BLACK; - } else if (scrimTransparent && barColor == Color.TRANSPARENT) { + } else if (scrimTransparent && Color.alpha(barColor) == 0) { boolean light = (sysuiVis & lightSysuiFlag) != 0; return light ? SCRIM_LIGHT : semiTransparentBarColor; } else { diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index fef4dcd3b9f1..eda6cd09d7f5 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -724,6 +724,10 @@ <!-- Allows an application to send SMS messages. <p>Protection level: dangerous + + <p> This is a hard restricted permission which cannot be held by an app until + the installer on record did not whitelist the permission. For more details see + {@link android.content.pm.PackageInstaller.SessionParams#setWhitelistedRestrictedPermissions(Set)}. --> <permission android:name="android.permission.SEND_SMS" android:permissionGroup="android.permission-group.UNDEFINED" @@ -734,6 +738,10 @@ <!-- Allows an application to receive SMS messages. <p>Protection level: dangerous + + <p> This is a hard restricted permission which cannot be held by an app until + the installer on record did not whitelist the permission. For more details see + {@link android.content.pm.PackageInstaller.SessionParams#setWhitelistedRestrictedPermissions(Set)}. --> <permission android:name="android.permission.RECEIVE_SMS" android:permissionGroup="android.permission-group.UNDEFINED" @@ -744,6 +752,10 @@ <!-- Allows an application to read SMS messages. <p>Protection level: dangerous + + <p> This is a hard restricted permission which cannot be held by an app until + the installer on record did not whitelist the permission. For more details see + {@link android.content.pm.PackageInstaller.SessionParams#setWhitelistedRestrictedPermissions(Set)}. --> <permission android:name="android.permission.READ_SMS" android:permissionGroup="android.permission-group.UNDEFINED" @@ -754,6 +766,10 @@ <!-- Allows an application to receive WAP push messages. <p>Protection level: dangerous + + <p> This is a hard restricted permission which cannot be held by an app until + the installer on record did not whitelist the permission. For more details see + {@link android.content.pm.PackageInstaller.SessionParams#setWhitelistedRestrictedPermissions(Set)}. --> <permission android:name="android.permission.RECEIVE_WAP_PUSH" android:permissionGroup="android.permission-group.UNDEFINED" @@ -763,7 +779,11 @@ android:protectionLevel="dangerous" /> <!-- Allows an application to monitor incoming MMS messages. - <p>Protection level: dangerous + <p>Protection level: dangerous + + <p> This is a hard restricted permission which cannot be held by an app until + the installer on record did not whitelist the permission. For more details see + {@link android.content.pm.PackageInstaller.SessionParams#setWhitelistedRestrictedPermissions(Set)}. --> <permission android:name="android.permission.RECEIVE_MMS" android:permissionGroup="android.permission-group.UNDEFINED" @@ -783,6 +803,11 @@ when the alert is first received, and to delay presenting the info to the user until after the initial alert dialog is dismissed. <p>Protection level: dangerous + + <p> This is a hard restricted permission which cannot be held by an app until + the installer on record did not whitelist the permission. For more details see + {@link android.content.pm.PackageInstaller.SessionParams#setWhitelistedRestrictedPermissions(Set)}. + @hide Pending API council approval --> <permission android:name="android.permission.READ_CELL_BROADCASTS" android:permissionGroup="android.permission-group.UNDEFINED" @@ -805,30 +830,35 @@ android:priority="900" /> <!-- Allows an application to read from external storage. - <p>Any app that declares the {@link #WRITE_EXTERNAL_STORAGE} permission is implicitly - granted this permission.</p> - <p>This permission is enforced starting in API level 19. Before API level 19, this - permission is not enforced and all apps still have access to read from external storage. - You can test your app with the permission enforced by enabling <em>Protect USB - storage</em> under Developer options in the Settings app on a device running Android 4.1 or - higher.</p> - <p>Also starting in API level 19, this permission is <em>not</em> required to - read/write files in your application-specific directories returned by - {@link android.content.Context#getExternalFilesDir} and - {@link android.content.Context#getExternalCacheDir}. - <p class="note"><strong>Note:</strong> If <em>both</em> your <a - href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#min">{@code - minSdkVersion}</a> and <a - href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#target">{@code - targetSdkVersion}</a> values are set to 3 or lower, the system implicitly - grants your app this permission. If you don't need this permission, be sure your <a - href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#target">{@code - targetSdkVersion}</a> is 4 or higher. - <p>Is this permission is not whitelisted for an app that targets an API level before - {@link android.os.Build.VERSION_CODES#Q} this permission cannot be granted to apps.</p> - <p>Is this permission is not whitelisted for an app that targets an API level - {@link android.os.Build.VERSION_CODES#Q} or later the app will be forced into isolated storage. - </p> + <p>Any app that declares the {@link #WRITE_EXTERNAL_STORAGE} permission is implicitly + granted this permission.</p> + <p>This permission is enforced starting in API level 19. Before API level 19, this + permission is not enforced and all apps still have access to read from external storage. + You can test your app with the permission enforced by enabling <em>Protect USB + storage</em> under Developer options in the Settings app on a device running Android 4.1 or + higher.</p> + <p>Also starting in API level 19, this permission is <em>not</em> required to + read/write files in your application-specific directories returned by + {@link android.content.Context#getExternalFilesDir} and + {@link android.content.Context#getExternalCacheDir}. + <p class="note"><strong>Note:</strong> If <em>both</em> your <a + href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#min">{@code + minSdkVersion}</a> and <a + href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#target">{@code + targetSdkVersion}</a> values are set to 3 or lower, the system implicitly + grants your app this permission. If you don't need this permission, be sure your <a + href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#target">{@code + targetSdkVersion}</a> is 4 or higher. + + <p> This is a soft restricted permission which cannot be held by an app it its + full form until the installer on record did not whitelist the permission. + Specifically, if the permission is whitelisted the holder app can access + external storage and the visual and aural media collections while if the + permission is not whitelisted the holder app can only access to the visual + and aural medial collections. Also the permission is immutably restricted + meaning that the whitelist state can be specified only at install time and + cannot change until the app is installed. For more details see + {@link android.content.pm.PackageInstaller.SessionParams#setWhitelistedRestrictedPermissions(Set)}. --> <permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:permissionGroup="android.permission-group.UNDEFINED" @@ -916,6 +946,10 @@ {@link #ACCESS_FINE_LOCATION}. Requesting this permission by itself doesn't give you location access. <p>Protection level: dangerous + + <p> This is a hard restricted permission which cannot be held by an app until + the installer on record did not whitelist the permission. For more details see + {@link android.content.pm.PackageInstaller.SessionParams#setWhitelistedRestrictedPermissions(Set)}. --> <permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" android:permissionGroup="android.permission-group.UNDEFINED" @@ -958,6 +992,10 @@ href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#target">{@code targetSdkVersion}</a> is 16 or higher.</p> <p>Protection level: dangerous + + <p> This is a hard restricted permission which cannot be held by an app until + the installer on record did not whitelist the permission. For more details see + {@link android.content.pm.PackageInstaller.SessionParams#setWhitelistedRestrictedPermissions(Set)}. --> <permission android:name="android.permission.READ_CALL_LOG" android:permissionGroup="android.permission-group.UNDEFINED" @@ -978,6 +1016,10 @@ href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#target">{@code targetSdkVersion}</a> is 16 or higher.</p> <p>Protection level: dangerous + + <p> This is a hard restricted permission which cannot be held by an app until + the installer on record did not whitelist the permission. For more details see + {@link android.content.pm.PackageInstaller.SessionParams#setWhitelistedRestrictedPermissions(Set)}. --> <permission android:name="android.permission.WRITE_CALL_LOG" android:permissionGroup="android.permission-group.UNDEFINED" @@ -991,6 +1033,10 @@ abort the call altogether. <p>Protection level: dangerous + <p> This is a hard restricted permission which cannot be held by an app until + the installer on record did not whitelist the permission. For more details see + {@link android.content.pm.PackageInstaller.SessionParams#setWhitelistedRestrictedPermissions(Set)}. + @deprecated Applications should use {@link android.telecom.CallRedirectionService} instead of the {@link android.content.Intent#ACTION_NEW_OUTGOING_CALL} broadcast. --> @@ -4616,8 +4662,9 @@ android:process=":ui"> </activity> <activity android:name="com.android.internal.app.PlatLogoActivity" - android:theme="@style/Theme.Wallpaper.NoTitleBar.Fullscreen" + android:theme="@style/Theme.DeviceDefault.DayNight" android:configChanges="orientation|keyboardHidden" + android:icon="@drawable/platlogo" android:process=":ui"> </activity> <activity android:name="com.android.internal.app.DisableCarModeActivity" diff --git a/core/res/res/drawable-nodpi/android_logotype.xml b/core/res/res/drawable-nodpi/android_logotype.xml new file mode 100644 index 000000000000..bd298e48ef34 --- /dev/null +++ b/core/res/res/drawable-nodpi/android_logotype.xml @@ -0,0 +1,42 @@ +<!-- +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. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="290dp" + android:height="64dp" + android:viewportWidth="290.0" + android:viewportHeight="64.0"> + <path + android:fillColor="#FF000000" + android:pathData="M21.81,28.91c-7.44,0,-12.45,5.85,-12.45,13.37c0,7.52,5.01,13.37,12.45,13.37s12.45,-5.85,12.45,-13.37 C34.26,34.76,29.24,28.91,21.81,28.91 M20.13,20.55c6.02,0,11.03,3.09,13.37,6.43v-5.6l9.19,0l0,41.78l-6.24,0 c-1.63,0,-2.95,-1.32,-2.95,-2.95v-2.65C31.17,60.91,26.15,64,20.14,64C8.69,64,0,54.23,0,42.28C0,30.33,8.69,20.55,20.13,20.55"/> + <path + android:fillColor="#FF000000" + android:pathData="M53.13,21.39l9.19,0l0,5.68c2.5,-4.18,7.27,-6.52,12.7,-6.52c9.69,0,15.96,6.85,15.96,17.46l0,25.15l-6.24,0 c-1.63,0,-2.95,-1.32,-2.95,-2.95l0,-20.7c0,-6.6,-3.34,-10.61,-8.69,-10.61c-6.1,0,-10.78,4.76,-10.78,13.7l0,20.55l-6.25,0 c-1.63,0,-2.95,-1.32,-2.95,-2.95L53.13,21.39z"/> + <path + android:fillColor="#FF000000" + android:pathData="M120.06,28.91c-7.43,0,-12.45,5.85,-12.45,13.37c0,7.52,5.01,13.37,12.45,13.37c7.43,0,12.45,-5.85,12.45,-13.37 C132.51,34.76,127.5,28.91,120.06,28.91 M118.39,20.55c6.02,0,11.03,3.09,13.37,6.43l0,-26.49l9.19,0l0,62.66h-6.24 c-1.63,0,-2.95,-1.32,-2.95,-2.95v-2.65c-2.34,3.34,-7.35,6.43,-13.37,6.43c-11.45,0,-20.14,-9.77,-20.14,-21.72 C98.25,30.33,106.94,20.55,118.39,20.55"/> + <path + android:fillColor="#FF000000" + android:pathData="M151.39,21.39l9.19,0v7.44c1.59,-4.76,6.27,-7.86,11.03,-7.86c1.17,0,2.34,0.08,3.59,0.34v9.44c-1.59,-0.5,-2.92,-0.75,-4.59,-0.75 c-5.26,0,-10.03,4.43,-10.03,12.78l0,20.39l-6.24,0c-1.63,0,-2.95,-1.32,-2.95,-2.95L151.39,21.39z"/> + <path + android:fillColor="#FF000000" + android:pathData="M199.98,55.48c7.35,0,12.53,-5.77,12.53,-13.2c0,-7.44,-5.18,-13.2,-12.53,-13.2c-7.44,0,-12.62,5.77,-12.62,13.2 C187.37,49.71,192.55,55.48,199.98,55.48 M199.98,64c-12.37,0,-21.89,-9.61,-21.89,-21.72c0,-12.12,9.52,-21.73,21.89,-21.73 c12.37,0,21.89,9.61,21.89,21.73C221.87,54.39,212.35,64,199.98,64"/> + <path + android:fillColor="#FF000000" + android:pathData="M229.32,21.39l9.19,0l0,41.78l-6.24,0c-1.63,0,-2.95,-1.32,-2.95,-2.95L229.32,21.39z M233.92,12.28 c-3.34,0,-6.18,-2.76,-6.18,-6.18c0,-3.34,2.84,-6.1,6.18,-6.1c3.43,0,6.1,2.76,6.1,6.1C240.02,9.53,237.34,12.28,233.92,12.28"/> + <path + android:fillColor="#FF000000" + android:pathData="M267.87,28.91c-7.43,0,-12.45,5.85,-12.45,13.37c0,7.52,5.01,13.37,12.45,13.37c7.44,0,12.45,-5.85,12.45,-13.37 C280.32,34.76,275.31,28.91,267.87,28.91 M266.2,20.55c6.02,0,11.03,3.09,13.37,6.43l0,-26.49l9.19,0l0,62.66l-6.24,0 c-1.63,0,-2.95,-1.32,-2.95,-2.95v-2.65c-2.34,3.34,-7.35,6.43,-13.37,6.43c-11.44,0,-20.14,-9.77,-20.14,-21.72S254.76,20.55,266.2,20.55"/> +</vector> diff --git a/core/res/res/drawable-nodpi/platlogo.xml b/core/res/res/drawable-nodpi/platlogo.xml index f5bbadcce06b..19a296a0e46b 100644 --- a/core/res/res/drawable-nodpi/platlogo.xml +++ b/core/res/res/drawable-nodpi/platlogo.xml @@ -1,5 +1,5 @@ <!-- -Copyright (C) 2018 The Android Open Source Project +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. @@ -13,21 +13,15 @@ Copyright (C) 2018 The Android Open Source Project See the License for the specific language governing permissions and limitations under the License. --> -<vector - xmlns:android="http://schemas.android.com/apk/res/android" - android:name="vector" - android:width="640dp" - android:height="640dp" - android:viewportWidth="64" - android:viewportHeight="64"> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> <path - android:name="bg" - android:pathData="M 27 43 L 32 43 C 38.075 43 43 38.075 43 32 C 43 25.925 38.075 21 32 21 C 25.925 21 21 25.925 21 32 L 21 64" - android:strokeColor="#6823a1" - android:strokeWidth="16"/> + android:fillColor="#FF000000" + android:pathData="M19.45,22.89l-10.250001,-10.249999l-2.6599998,2.6599998l-1.77,-1.7600002l4.43,-4.4300003l12.0199995,12.0199995l-1.7699986,1.7600002z"/> <path - android:name="fg" - android:pathData="M 29 43 L 32 43 C 38.075 43 43 38.075 43 32 C 43 25.925 38.075 21 32 21 C 25.925 21 21 25.925 21 32 L 21 64" - android:strokeColor="#ff0000" - android:strokeWidth="8"/> + android:fillColor="#FF000000" + android:pathData="M12,6a6,6 0,1 1,-6 6,6 6,0 0,1 6,-6m0,-2.5A8.5,8.5 0,1 0,20.5 12,8.51 8.51,0 0,0 12,3.5Z"/> </vector> diff --git a/core/res/res/layout/platlogo_layout.xml b/core/res/res/layout/platlogo_layout.xml new file mode 100644 index 000000000000..4a4ad751e421 --- /dev/null +++ b/core/res/res/layout/platlogo_layout.xml @@ -0,0 +1,50 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2019 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. +--> +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:clipChildren="false" + android:background="@android:color/transparent"> + <ImageView + android:id="@+id/text" + android:layout_width="400dp" + android:layout_height="wrap_content" + android:translationY="-100dp" + android:adjustViewBounds="true" + android:layout_marginBottom="-80dp" + android:layout_centerInParent="true" + android:src="@drawable/android_logotype" + android:tint="?android:attr/textColorPrimary" + /> + <ImageView + android:id="@+id/one" + android:layout_width="200dp" + android:layout_height="200dp" + android:layout_marginLeft="24dp" + android:layout_below="@id/text" + android:layout_alignLeft="@id/text" + android:tint="?android:attr/textColorPrimary" + /> + <ImageView + android:id="@+id/zero" + android:layout_width="200dp" + android:layout_height="200dp" + android:layout_marginRight="34dp" + android:layout_below="@id/text" + android:layout_alignRight="@id/text" + android:tint="?android:attr/textColorPrimary" + /> +</RelativeLayout> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 544fd4457c7a..0367c279a77e 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -3818,4 +3818,8 @@ <java-symbol type="string" name="config_defaultSupervisionProfileOwnerComponent" /> <java-symbol type="bool" name="config_inflateSignalStrength" /> + + <java-symbol type="drawable" name="android_logotype" /> + <java-symbol type="layout" name="platlogo_layout" /> + </resources> diff --git a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java index 711eaa7edc2a..c50cbe3773ab 100644 --- a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java +++ b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java @@ -19,6 +19,8 @@ package android.app.activity; import static android.content.Intent.ACTION_EDIT; import static android.content.Intent.ACTION_VIEW; +import static com.google.common.truth.Truth.assertThat; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNull; @@ -31,6 +33,7 @@ import android.app.servertransaction.ActivityConfigurationChangeItem; import android.app.servertransaction.ActivityRelaunchItem; import android.app.servertransaction.ClientTransaction; import android.app.servertransaction.ClientTransactionItem; +import android.app.servertransaction.NewIntentItem; import android.app.servertransaction.ResumeActivityItem; import android.app.servertransaction.StopActivityItem; import android.content.Intent; @@ -45,9 +48,13 @@ import androidx.test.filters.MediumTest; import androidx.test.rule.ActivityTestRule; import androidx.test.runner.AndroidJUnit4; +import com.android.internal.content.ReferrerIntent; + import org.junit.Test; import org.junit.runner.RunWith; +import java.util.ArrayList; +import java.util.List; import java.util.concurrent.CountDownLatch; /** @@ -307,6 +314,24 @@ public class ActivityThreadTest { assertEquals(400, activity.mConfig.smallestScreenWidthDp); } + @Test + public void testResumeAfterNewIntent() { + final Activity activity = mActivityTestRule.launchActivity(new Intent()); + final ActivityThread activityThread = activity.getActivityThread(); + final ArrayList<ReferrerIntent> rIntents = new ArrayList<>(); + rIntents.add(new ReferrerIntent(new Intent(), "android.app.activity")); + + InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { + activityThread.executeTransaction(newNewIntentTransaction(activity, rIntents, false)); + }); + assertThat(activity.isResumed()).isFalse(); + + InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { + activityThread.executeTransaction(newNewIntentTransaction(activity, rIntents, true)); + }); + assertThat(activity.isResumed()).isTrue(); + } + /** * Calls {@link ActivityThread#handleActivityConfigurationChanged(IBinder, Configuration, int)} * to try to push activity configuration to the activity for the given sequence number. @@ -386,6 +411,16 @@ public class ActivityThreadTest { return transaction; } + private static ClientTransaction newNewIntentTransaction(Activity activity, + List<ReferrerIntent> intents, boolean resume) { + final NewIntentItem item = NewIntentItem.obtain(intents, resume); + + final ClientTransaction transaction = newTransaction(activity); + transaction.addCallback(item); + + return transaction; + } + private static ClientTransaction newTransaction(Activity activity) { final IApplicationThread appThread = activity.getActivityThread().getApplicationThread(); return ClientTransaction.obtain(appThread, activity.getActivityToken()); diff --git a/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java b/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java index 1e49c0a7f55d..37d21f0928be 100644 --- a/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java +++ b/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java @@ -214,15 +214,15 @@ public class ObjectPoolTests { @Test public void testRecycleNewIntentItem() { - NewIntentItem emptyItem = NewIntentItem.obtain(null); - NewIntentItem item = NewIntentItem.obtain(referrerIntentList()); + NewIntentItem emptyItem = NewIntentItem.obtain(null, false); + NewIntentItem item = NewIntentItem.obtain(referrerIntentList(), false); assertNotSame(item, emptyItem); assertFalse(item.equals(emptyItem)); item.recycle(); assertEquals(item, emptyItem); - NewIntentItem item2 = NewIntentItem.obtain(referrerIntentList()); + NewIntentItem item2 = NewIntentItem.obtain(referrerIntentList(), false); assertSame(item, item2); assertFalse(item2.equals(emptyItem)); } diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java index 36ed88fbe8d5..d2b18cb0bcb8 100644 --- a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java +++ b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java @@ -128,7 +128,7 @@ public class TransactionParcelTests { @Test public void testNewIntent() { // Write to parcel - NewIntentItem item = NewIntentItem.obtain(referrerIntentList()); + NewIntentItem item = NewIntentItem.obtain(referrerIntentList(), false); writeAndPrepareForReading(item); // Read from parcel and assert diff --git a/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheTest.java b/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheTest.java index c8150b12a23b..365e97ded928 100644 --- a/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheTest.java +++ b/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheTest.java @@ -16,7 +16,6 @@ package android.content.pm; -import android.content.Intent; import android.content.res.Resources; import android.os.FileUtils; import android.os.Parcel; @@ -190,36 +189,6 @@ public class RegisteredServicesCacheTest extends AndroidTestCase { assertEquals(0, cache.getPersistentServicesSize(u1)); } - /** - * Check that an optimization to skip a call to PackageManager handles an invalidated cache. - * - * We added an optimization in generateServicesMap to only query PackageManager for packages - * that have been changed, because if a package is unchanged, we have already cached the - * services info for it, so we can save a query to PackageManager (and save some memory). - * However, if invalidateCache was called, we cannot optimize, and must do a full query. - * The initial optimization was buggy because it failed to check for an invalidated cache, and - * only scanned the changed packages, given in the ACTION_PACKAGE_CHANGED intent (b/122912184). - */ - public void testParseServiceInfoOptimizationHandlesInvalidatedCache() { - TestServicesCache cache = new TestServicesCache(); - cache.addServiceForQuerying(U0, r1, newServiceInfo(t1, UID1)); - cache.addServiceForQuerying(U0, r2, newServiceInfo(t2, UID2)); - assertEquals(2, cache.getAllServicesSize(U0)); - - // simulate the client of the cache invalidating it - cache.invalidateCache(U0); - - // there should be 0 services (userServices.services == null ) at this point, but we don't - // call getAllServicesSize since that would force a full scan of packages, - // instead we trigger a package change in a package that is in the list of services - Intent intent = new Intent(Intent.ACTION_PACKAGE_CHANGED); - intent.putExtra(Intent.EXTRA_UID, UID1); - cache.handlePackageEvent(intent, U0); - - // check that the optimization does a full query and caches both services - assertEquals(2, cache.getAllServicesSize(U0)); - } - private static RegisteredServicesCache.ServiceInfo<TestServiceType> newServiceInfo( TestServiceType type, int uid) { final ComponentInfo info = new ComponentInfo(); @@ -297,11 +266,6 @@ public class RegisteredServicesCacheTest extends AndroidTestCase { map = new HashMap<>(); mServices.put(userId, map); } - // in actual cases, resolveInfo should always have a serviceInfo, since we specifically - // query for intent services - resolveInfo.serviceInfo = new android.content.pm.ServiceInfo(); - resolveInfo.serviceInfo.applicationInfo = - new ApplicationInfo(serviceInfo.componentInfo.applicationInfo); map.put(resolveInfo, serviceInfo); } @@ -340,11 +304,6 @@ public class RegisteredServicesCacheTest extends AndroidTestCase { public void onUserRemoved(int userId) { super.onUserRemoved(userId); } - - @Override - public void handlePackageEvent(Intent intent, int userId) { - super.handlePackageEvent(intent, userId); - } } static class TestSerializer implements XmlSerializerAndParser<TestServiceType> { diff --git a/core/tests/coretests/src/android/os/RedactingFileDescriptorTest.java b/core/tests/coretests/src/android/os/RedactingFileDescriptorTest.java index d5163e193510..eff4826040f4 100644 --- a/core/tests/coretests/src/android/os/RedactingFileDescriptorTest.java +++ b/core/tests/coretests/src/android/os/RedactingFileDescriptorTest.java @@ -64,7 +64,7 @@ public class RedactingFileDescriptorTest { @Test public void testSingleByte() throws Exception { final FileDescriptor fd = RedactingFileDescriptor.open(mContext, mFile, MODE_READ_ONLY, - new long[] { 10, 11 }).getFileDescriptor(); + new long[] { 10, 11 }, new long[] {}).getFileDescriptor(); final byte[] buf = new byte[1_000]; assertEquals(buf.length, Os.read(fd, buf, 0, buf.length)); @@ -80,7 +80,7 @@ public class RedactingFileDescriptorTest { @Test public void testRanges() throws Exception { final FileDescriptor fd = RedactingFileDescriptor.open(mContext, mFile, MODE_READ_ONLY, - new long[] { 100, 200, 300, 400 }).getFileDescriptor(); + new long[] { 100, 200, 300, 400 }, new long[] {}).getFileDescriptor(); final byte[] buf = new byte[10]; assertEquals(buf.length, Os.pread(fd, buf, 0, 10, 90)); @@ -102,7 +102,7 @@ public class RedactingFileDescriptorTest { @Test public void testEntireFile() throws Exception { final FileDescriptor fd = RedactingFileDescriptor.open(mContext, mFile, MODE_READ_ONLY, - new long[] { 0, 5_000_000 }).getFileDescriptor(); + new long[] { 0, 5_000_000 }, new long[] {}).getFileDescriptor(); try (FileInputStream in = new FileInputStream(fd)) { int val; @@ -115,7 +115,7 @@ public class RedactingFileDescriptorTest { @Test public void testReadWrite() throws Exception { final FileDescriptor fd = RedactingFileDescriptor.open(mContext, mFile, MODE_READ_WRITE, - new long[] { 100, 200, 300, 400 }).getFileDescriptor(); + new long[] { 100, 200, 300, 400 }, new long[] {}).getFileDescriptor(); // Redacted at first final byte[] buf = new byte[10]; @@ -168,4 +168,76 @@ public class RedactingFileDescriptorTest { assertArrayEquals(new long[] { 100, 200 }, removeRange(new long[] { 100, 200 }, 150, 150)); } + + @Test + public void testFreeAtStart() throws Exception { + final FileDescriptor fd = RedactingFileDescriptor.open(mContext, mFile, MODE_READ_WRITE, + new long[] { 1, 10 }, new long[] {1}).getFileDescriptor(); + + final byte[] buf = new byte[10]; + assertEquals(buf.length, Os.pread(fd, buf, 0, 10, 0)); + assertArrayEquals( + new byte[] { 64, (byte) 'f', (byte) 'r', (byte) 'e', (byte) 'e', 0, 0, 0, 0, 0 }, + buf); + } + + @Test + public void testFreeAtOffset() throws Exception { + final FileDescriptor fd = RedactingFileDescriptor.open(mContext, mFile, MODE_READ_WRITE, + new long[] { 1, 10 }, new long[] {3}).getFileDescriptor(); + + final byte[] buf = new byte[10]; + assertEquals(buf.length, Os.pread(fd, buf, 0, 10, 0)); + assertArrayEquals( + new byte[] { 64, 0, 0, (byte) 'f', (byte) 'r', (byte) 'e', (byte) 'e', 0, 0, 0 }, + buf); + } + + @Test + public void testFreeAcrossRedactionStart() throws Exception { + final FileDescriptor fd = RedactingFileDescriptor.open(mContext, mFile, MODE_READ_WRITE, + new long[] { 1, 10 }, new long[] {0}).getFileDescriptor(); + + final byte[] buf = new byte[10]; + assertEquals(buf.length, Os.pread(fd, buf, 0, 10, 0)); + assertArrayEquals( + new byte[] { 64, (byte) 'r', (byte) 'e', (byte) 'e', 0, 0, 0, 0, 0, 0 }, + buf); + } + + @Test + public void testFreeAcrossRedactionEnd() throws Exception { + final FileDescriptor fd = RedactingFileDescriptor.open(mContext, mFile, MODE_READ_WRITE, + new long[] { 1, 3 }, new long[] {2}).getFileDescriptor(); + + final byte[] buf = new byte[10]; + assertEquals(buf.length, Os.pread(fd, buf, 0, 10, 0)); + assertArrayEquals( + new byte[] { 64, 0, (byte) 'f', 64, 64, 64, 64, 64, 64, 64 }, + buf); + } + + @Test + public void testFreeOutsideRedaction() throws Exception { + final FileDescriptor fd = RedactingFileDescriptor.open(mContext, mFile, MODE_READ_WRITE, + new long[] { 1, 8 }, new long[] { 8 }).getFileDescriptor(); + + final byte[] buf = new byte[10]; + assertEquals(buf.length, Os.pread(fd, buf, 0, 10, 0)); + assertArrayEquals( + new byte[] { 64, 0, 0, 0, 0, 0, 0, 0, 64, 64 }, + buf); + } + + @Test + public void testFreeMultipleRedactions() throws Exception { + final FileDescriptor fd = RedactingFileDescriptor.open(mContext, mFile, MODE_READ_WRITE, + new long[] { 1, 2, 3, 4 }, new long[] { 0 }).getFileDescriptor(); + + final byte[] buf = new byte[10]; + assertEquals(buf.length, Os.pread(fd, buf, 0, 10, 0)); + assertArrayEquals( + new byte[] { 64, (byte) 'r', 64, (byte) 'e', 64, 64, 64, 64, 64, 64 }, + buf); + } } diff --git a/libs/androidfw/PosixUtils.cpp b/libs/androidfw/PosixUtils.cpp index df0dd7ce463d..f1ab1493012a 100644 --- a/libs/androidfw/PosixUtils.cpp +++ b/libs/androidfw/PosixUtils.cpp @@ -64,6 +64,9 @@ std::unique_ptr<ProcResult> ExecuteBinary(const std::vector<std::string>& argv) return nullptr; } + auto gid = getgid(); + auto uid = getuid(); + char const** argv0 = (char const**)malloc(sizeof(char*) * (argv.size() + 1)); for (size_t i = 0; i < argv.size(); i++) { argv0[i] = argv[i].c_str(); @@ -75,6 +78,16 @@ std::unique_ptr<ProcResult> ExecuteBinary(const std::vector<std::string>& argv) PLOG(ERROR) << "fork"; return nullptr; case 0: // child + if (setgid(gid) != 0) { + PLOG(ERROR) << "setgid"; + exit(1); + } + + if (setuid(uid) != 0) { + PLOG(ERROR) << "setuid"; + exit(1); + } + close(stdout[0]); if (dup2(stdout[1], STDOUT_FILENO) == -1) { abort(); diff --git a/media/apex/java/android/media/MediaController2.java b/media/apex/java/android/media/MediaController2.java index 63a4510e7fe7..c3dd3fe4451c 100644 --- a/media/apex/java/android/media/MediaController2.java +++ b/media/apex/java/android/media/MediaController2.java @@ -46,14 +46,14 @@ import android.util.Log; import java.util.concurrent.Executor; /** + * This API is not generally intended for third party application developers. + * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a> + * <a href="{@docRoot}reference/androidx/media2/session/package-summary.html">Media2 session + * Library</a> for consistent behavior across all devices. + * * Allows an app to interact with an active {@link MediaSession2} or a * {@link MediaSession2Service} which would provide {@link MediaSession2}. Media buttons and other * commands can be sent to the session. - * <p> - * This API is not generally intended for third party application developers. - * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a> - * <a href="{@docRoot}reference/androidx/media2/package-summary.html">Media2 Library</a> - * for consistent behavior across all devices. */ public class MediaController2 implements AutoCloseable { static final String TAG = "MediaController2"; @@ -405,6 +405,11 @@ public class MediaController2 implements AutoCloseable { } /** + * This API is not generally intended for third party application developers. + * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a> + * <a href="{@docRoot}reference/androidx/media2/session/package-summary.html">Media2 session + * Library</a> for consistent behavior across all devices. + * <p> * Builder for {@link MediaController2}. * <p> * Any incoming event from the {@link MediaSession2} will be handled on the callback @@ -502,9 +507,12 @@ public class MediaController2 implements AutoCloseable { } /** - * Interface for listening to change in activeness of the {@link MediaSession2}. - * <p> * This API is not generally intended for third party application developers. + * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a> + * <a href="{@docRoot}reference/androidx/media2/session/package-summary.html">Media2 session + * Library</a> for consistent behavior across all devices. + * <p> + * Interface for listening to change in activeness of the {@link MediaSession2}. */ public abstract static class ControllerCallback { /** diff --git a/media/apex/java/android/media/MediaSession2.java b/media/apex/java/android/media/MediaSession2.java index b3edf3f50866..081e76ab0215 100644 --- a/media/apex/java/android/media/MediaSession2.java +++ b/media/apex/java/android/media/MediaSession2.java @@ -52,13 +52,13 @@ import java.util.Objects; import java.util.concurrent.Executor; /** - * Allows a media app to expose its transport controls and playback information in a process to - * other processes including the Android framework and other apps. - * <p> * This API is not generally intended for third party application developers. * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a> - * <a href="{@docRoot}reference/androidx/media2/package-summary.html">Media2 Library</a> - * for consistent behavior across all devices. + * <a href="{@docRoot}reference/androidx/media2/session/package-summary.html">Media2 session + * Library</a> for consistent behavior across all devices. + * <p> + * Allows a media app to expose its transport controls and playback information in a process to + * other processes including the Android framework and other apps. */ public class MediaSession2 implements AutoCloseable { static final String TAG = "MediaSession2"; @@ -481,6 +481,11 @@ public class MediaSession2 implements AutoCloseable { } /** + * This API is not generally intended for third party application developers. + * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a> + * <a href="{@docRoot}reference/androidx/media2/session/package-summary.html">Media2 session + * Library</a> for consistent behavior across all devices. + * <p> * Builder for {@link MediaSession2}. * <p> * Any incoming event from the {@link MediaController2} will be handled on the callback @@ -616,9 +621,12 @@ public class MediaSession2 implements AutoCloseable { } /** - * Information of a controller. - * <p> * This API is not generally intended for third party application developers. + * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a> + * <a href="{@docRoot}reference/androidx/media2/session/package-summary.html">Media2 session + * Library</a> for consistent behavior across all devices. + * <p> + * Information of a controller. */ public static final class ControllerInfo { private final RemoteUserInfo mRemoteUserInfo; @@ -807,9 +815,12 @@ public class MediaSession2 implements AutoCloseable { } /** - * Callback to be called for all incoming commands from {@link MediaController2}s. - * <p> * This API is not generally intended for third party application developers. + * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a> + * <a href="{@docRoot}reference/androidx/media2/session/package-summary.html">Media2 session + * Library</a> for consistent behavior across all devices. + * <p> + * Callback to be called for all incoming commands from {@link MediaController2}s. */ public abstract static class SessionCallback { /** diff --git a/media/apex/java/android/media/MediaSession2Service.java b/media/apex/java/android/media/MediaSession2Service.java index ee584e5eac30..f6fd509fd245 100644 --- a/media/apex/java/android/media/MediaSession2Service.java +++ b/media/apex/java/android/media/MediaSession2Service.java @@ -44,12 +44,12 @@ import java.util.List; import java.util.Map; /** - * Service containing {@link MediaSession2}. - * <p> * This API is not generally intended for third party application developers. * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a> - * <a href="{@docRoot}reference/androidx/media2/package-summary.html">Media2 Library</a> - * for consistent behavior across all devices. + * <a href="{@docRoot}reference/androidx/media2/session/package-summary.html">Media2 session + * Library</a> for consistent behavior across all devices. + * <p> + * Service containing {@link MediaSession2}. */ public abstract class MediaSession2Service extends Service { /** @@ -287,6 +287,11 @@ public abstract class MediaSession2Service extends Service { } /** + * This API is not generally intended for third party application developers. + * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a> + * <a href="{@docRoot}reference/androidx/media2/session/package-summary.html">Media2 session + * Library</a> for consistent behavior across all devices. + * <p> * Returned by {@link #onUpdateNotification(MediaSession2)} for making session service * foreground service to keep playback running in the background. It's highly recommended to * show media style notification here. diff --git a/media/apex/java/android/media/Session2Command.java b/media/apex/java/android/media/Session2Command.java index 7c752e198f3a..26f4568fa7e5 100644 --- a/media/apex/java/android/media/Session2Command.java +++ b/media/apex/java/android/media/Session2Command.java @@ -26,6 +26,11 @@ import android.text.TextUtils; import java.util.Objects; /** + * This API is not generally intended for third party application developers. + * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a> + * <a href="{@docRoot}reference/androidx/media2/session/package-summary.html">Media2 session + * Library</a> for consistent behavior across all devices. + * <p> * Define a command that a {@link MediaController2} can send to a {@link MediaSession2}. * <p> * If {@link #getCommandCode()} isn't {@link #COMMAND_CODE_CUSTOM}), it's predefined command. @@ -35,11 +40,6 @@ import java.util.Objects; * Refer to the * <a href="{@docRoot}reference/androidx/media2/SessionCommand2.html">AndroidX SessionCommand</a> * class for the list of valid commands. - * <p> - * This API is not generally intended for third party application developers. - * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a> - * <a href="{@docRoot}reference/androidx/media2/package-summary.html">Media2 Library</a> - * for consistent behavior across all devices. */ public final class Session2Command implements Parcelable { /** @@ -162,6 +162,11 @@ public final class Session2Command implements Parcelable { } /** + * This API is not generally intended for third party application developers. + * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a> + * <a href="{@docRoot}reference/androidx/media2/session/package-summary.html">Media2 session + * Library</a> for consistent behavior across all devices. + * <p> * Contains the result of {@link Session2Command}. */ public static final class Result { diff --git a/media/apex/java/android/media/Session2CommandGroup.java b/media/apex/java/android/media/Session2CommandGroup.java index 06ae8737fdc0..0ee5f62be128 100644 --- a/media/apex/java/android/media/Session2CommandGroup.java +++ b/media/apex/java/android/media/Session2CommandGroup.java @@ -28,13 +28,12 @@ import java.util.HashSet; import java.util.Set; /** - * A set of {@link Session2Command} which represents a command group. - * <p> * This API is not generally intended for third party application developers. * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a> - * <a href="{@docRoot}reference/androidx/media2/package-summary.html">Media2 Library</a> - * for consistent behavior across all devices. - * </p> + * <a href="{@docRoot}reference/androidx/media2/session/package-summary.html">Media2 session + * Library</a> for consistent behavior across all devices. + * <p> + * A set of {@link Session2Command} which represents a command group. */ public final class Session2CommandGroup implements Parcelable { private static final String TAG = "Session2CommandGroup"; @@ -131,6 +130,11 @@ public final class Session2CommandGroup implements Parcelable { } /** + * This API is not generally intended for third party application developers. + * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a> + * <a href="{@docRoot}reference/androidx/media2/session/package-summary.html">Media2 session + * Library</a> for consistent behavior across all devices. + * <p> * Builds a {@link Session2CommandGroup} object. */ public static final class Builder { diff --git a/media/apex/java/android/media/Session2Token.java b/media/apex/java/android/media/Session2Token.java index 6d499fa88815..6eb76b11497e 100644 --- a/media/apex/java/android/media/Session2Token.java +++ b/media/apex/java/android/media/Session2Token.java @@ -36,13 +36,13 @@ import java.util.List; import java.util.Objects; /** - * Represents an ongoing {@link MediaSession2} or a {@link MediaSession2Service}. - * If it's representing a session service, it may not be ongoing. - * <p> * This API is not generally intended for third party application developers. * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a> - * <a href="{@docRoot}reference/androidx/media2/package-summary.html">Media2 Library</a> - * for consistent behavior across all devices. + * <a href="{@docRoot}reference/androidx/media2/session/package-summary.html">Media2 session + * Library</a> for consistent behavior across all devices. + * <p> + * Represents an ongoing {@link MediaSession2} or a {@link MediaSession2Service}. + * If it's representing a session service, it may not be ongoing. * <p> * This may be passed to apps by the session owner to allow them to create a * {@link MediaController2} to communicate with the session. diff --git a/media/java/android/media/session/MediaSessionManager.java b/media/java/android/media/session/MediaSessionManager.java index 569d11e1dce6..dec0140c0d5c 100644 --- a/media/java/android/media/session/MediaSessionManager.java +++ b/media/java/android/media/session/MediaSessionManager.java @@ -119,6 +119,11 @@ public final class MediaSessionManager { } /** + * This API is not generally intended for third party application developers. + * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a> + * <a href="{@docRoot}reference/androidx/media2/session/package-summary.html">Media2 session + * Library</a> for consistent behavior across all devices. + * <p> * Notifies that a new {@link MediaSession2} with type {@link Session2Token#TYPE_SESSION} is * created. * <p> @@ -192,6 +197,11 @@ public final class MediaSessionManager { } /** + * This API is not generally intended for third party application developers. + * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a> + * <a href="{@docRoot}reference/androidx/media2/session/package-summary.html">Media2 session + * Library</a> for consistent behavior across all devices. + * <p> * Gets a list of {@link Session2Token} with type {@link Session2Token#TYPE_SESSION} for the * current user. * <p> @@ -335,12 +345,12 @@ public final class MediaSessionManager { } /** - * Adds a listener to be notified when the {@link #getSession2Tokens()} changes. - * <p> * This API is not generally intended for third party application developers. * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a> - * <a href="{@docRoot}reference/androidx/media2/package-summary.html">Media2 Library</a> - * for consistent behavior across all devices. + * <a href="{@docRoot}reference/androidx/media2/session/package-summary.html">Media2 session + * Library</a> for consistent behavior across all devices. + * <p> + * Adds a listener to be notified when the {@link #getSession2Tokens()} changes. * * @param listener The listener to add */ @@ -350,12 +360,12 @@ public final class MediaSessionManager { } /** - * Adds a listener to be notified when the {@link #getSession2Tokens()} changes. - * <p> * This API is not generally intended for third party application developers. * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a> - * <a href="{@docRoot}reference/androidx/media2/package-summary.html">Media2 Library</a> - * for consistent behavior across all devices. + * <a href="{@docRoot}reference/androidx/media2/session/package-summary.html">Media2 session + * Library</a> for consistent behavior across all devices. + * <p> + * Adds a listener to be notified when the {@link #getSession2Tokens()} changes. * * @param listener The listener to add * @param handler The handler to call listener on. @@ -366,12 +376,12 @@ public final class MediaSessionManager { } /** - * Adds a listener to be notified when the {@link #getSession2Tokens()} changes. - * <p> * This API is not generally intended for third party application developers. * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a> - * <a href="{@docRoot}reference/androidx/media2/package-summary.html">Media2 Library</a> - * for consistent behavior across all devices. + * <a href="{@docRoot}reference/androidx/media2/session/package-summary.html">Media2 session + * Library</a> for consistent behavior across all devices. + * <p> + * Adds a listener to be notified when the {@link #getSession2Tokens()} changes. * * @param userId The userId to listen for changes on * @param listener The listener to add @@ -402,6 +412,11 @@ public final class MediaSessionManager { } /** + * This API is not generally intended for third party application developers. + * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a> + * <a href="{@docRoot}reference/androidx/media2/session/package-summary.html">Media2 session + * Library</a> for consistent behavior across all devices. + * <p> * Removes the {@link OnSession2TokensChangedListener} to stop receiving session token updates. * * @param listener The listener to remove. @@ -765,13 +780,13 @@ public final class MediaSessionManager { } /** - * Listens for changes to the {@link #getSession2Tokens()}. This can be added - * using {@link #addOnSession2TokensChangedListener(OnSession2TokensChangedListener, Handler)}. - * <p> * This API is not generally intended for third party application developers. * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a> - * <a href="{@docRoot}reference/androidx/media2/package-summary.html">Media2 Library</a> - * for consistent behavior across all devices. + * <a href="{@docRoot}reference/androidx/media2/session/package-summary.html">Media2 session + * Library</a> for consistent behavior across all devices. + * <p> + * Listens for changes to the {@link #getSession2Tokens()}. This can be added + * using {@link #addOnSession2TokensChangedListener(OnSession2TokensChangedListener, Handler)}. */ public interface OnSession2TokensChangedListener { /** diff --git a/packages/EasterEgg/AndroidManifest.xml b/packages/EasterEgg/AndroidManifest.xml index c7dd40d7afdb..7f76a4529963 100644 --- a/packages/EasterEgg/AndroidManifest.xml +++ b/packages/EasterEgg/AndroidManifest.xml @@ -1,40 +1,38 @@ <?xml version="1.0" encoding="utf-8"?> -<!-- - Copyright (C) 2018 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.egg" android:versionCode="1" android:versionName="1.0"> - <uses-sdk android:minSdkVersion="28" /> + <uses-permission android:name="android.permission.WRITE_SETTINGS" /> <application - android:icon="@drawable/icon" + android:icon="@drawable/q_icon" android:label="@string/app_name"> + <activity android:name=".quares.QuaresActivity" + android:icon="@drawable/q_icon" + android:label="@string/q_egg_name" + android:theme="@style/QuaresTheme"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.DEFAULT" /> + <!-- <category android:name="android.intent.category.LAUNCHER" /> --> + <category android:name="com.android.internal.category.PLATLOGO" /> + </intent-filter> + </activity> <activity android:name=".paint.PaintActivity" android:configChanges="orientation|keyboardHidden|screenSize|uiMode" - android:label="@string/app_name" + android:icon="@drawable/p_icon" + android:label="@string/p_egg_name" android:theme="@style/AppTheme"> <intent-filter> <action android:name="android.intent.action.MAIN" /> - <category android:name="android.intent.category.DEFAULT" /> - <!--<category android:name="android.intent.category.LAUNCHER" />--> - <category android:name="com.android.internal.category.PLATLOGO" /> + + <!-- <category android:name="android.intent.category.DEFAULT" /> --> + <!-- <category android:name="android.intent.category.LAUNCHER" /> --> + <!-- <category android:name="com.android.internal.category.PLATLOGO" /> --> </intent-filter> </activity> </application> diff --git a/packages/EasterEgg/res/drawable/icon_bg.xml b/packages/EasterEgg/res/drawable/icon_bg.xml index c1553ce50946..659f98be4f43 100644 --- a/packages/EasterEgg/res/drawable/icon_bg.xml +++ b/packages/EasterEgg/res/drawable/icon_bg.xml @@ -15,4 +15,4 @@ limitations under the License. --> <color xmlns:android="http://schemas.android.com/apk/res/android" - android:color="#C5E1A5" />
\ No newline at end of file + android:color="@color/q_clue_text" /> diff --git a/packages/EasterEgg/res/drawable/icon.xml b/packages/EasterEgg/res/drawable/p_icon.xml index 2306b7b554c5..2306b7b554c5 100644 --- a/packages/EasterEgg/res/drawable/icon.xml +++ b/packages/EasterEgg/res/drawable/p_icon.xml diff --git a/packages/EasterEgg/res/drawable/pixel_bg.xml b/packages/EasterEgg/res/drawable/pixel_bg.xml new file mode 100644 index 000000000000..4d4a113cce53 --- /dev/null +++ b/packages/EasterEgg/res/drawable/pixel_bg.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright 2019 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<selector xmlns:android="http://schemas.android.com/apk/res/android" + android:exitFadeDuration="100"> + <item android:state_pressed="true"> + <shape><solid android:color="@color/red"/></shape> + </item> + <item android:state_checked="true"> + <shape><solid android:color="@color/pixel_on"/></shape> + </item> + <item> + <shape><solid android:color="@color/pixel_off"/></shape> + </item> +</selector>
\ No newline at end of file diff --git a/packages/EasterEgg/res/drawable/q.xml b/packages/EasterEgg/res/drawable/q.xml new file mode 100644 index 000000000000..75baa47e3aa6 --- /dev/null +++ b/packages/EasterEgg/res/drawable/q.xml @@ -0,0 +1,27 @@ +<!-- +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. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + <path + android:fillColor="@color/q_icon_fg" + android:pathData="M19.45,22.89l-10.250001,-10.249999l-2.6599998,2.6599998l-1.77,-1.7600002l4.43,-4.4300003l12.0199995,12.0199995l-1.7699986,1.7600002z"/> + <path + android:fillColor="@color/q_icon_fg" + android:pathData="M12,6a6,6 0,1 1,-6 6,6 6,0 0,1 6,-6m0,-2.5A8.5,8.5 0,1 0,20.5 12,8.51 8.51,0 0,0 12,3.5Z"/> +</vector> diff --git a/packages/EasterEgg/res/drawable/q_icon.xml b/packages/EasterEgg/res/drawable/q_icon.xml new file mode 100644 index 000000000000..ef4b0a362043 --- /dev/null +++ b/packages/EasterEgg/res/drawable/q_icon.xml @@ -0,0 +1,19 @@ +<!-- + Copyright (C) 2019 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. +--> +<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> + <background android:drawable="@drawable/icon_bg"/> + <foreground android:drawable="@drawable/q_smaller"/> +</adaptive-icon> diff --git a/packages/EasterEgg/res/drawable/q_smaller.xml b/packages/EasterEgg/res/drawable/q_smaller.xml new file mode 100644 index 000000000000..c71dff094235 --- /dev/null +++ b/packages/EasterEgg/res/drawable/q_smaller.xml @@ -0,0 +1,23 @@ +<!-- +Copyright (C) 2019 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. +--> +<inset xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:insetBottom="5dp" + android:insetLeft="5dp" + android:insetRight="5dp" + android:insetTop="5dp" + android:drawable="@drawable/q" /> diff --git a/packages/EasterEgg/res/layout/activity_quares.xml b/packages/EasterEgg/res/layout/activity_quares.xml new file mode 100644 index 000000000000..dcc90f6f77ae --- /dev/null +++ b/packages/EasterEgg/res/layout/activity_quares.xml @@ -0,0 +1,46 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright 2019 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. +--> +<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:animateLayoutChanges="true" + tools:context="com.android.egg.quares.QuaresActivity"> + + <GridLayout + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center" + android:alignmentMode="alignBounds" + android:id="@+id/grid" + /> + + <Button + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:id="@+id/label" + android:layout_gravity="center_horizontal|bottom" + android:gravity="center" + android:textSize="18dp" + android:visibility="gone" + android:drawablePadding="8dp" + android:padding="12dp" + android:backgroundTint="@color/q_clue_bg_correct" + android:textColor="@color/q_clue_text" + android:layout_marginBottom="48dp" + android:elevation="30dp" + /> +</FrameLayout> diff --git a/packages/EasterEgg/res/values-night/q_colors.xml b/packages/EasterEgg/res/values-night/q_colors.xml new file mode 100644 index 000000000000..191bd944b7dc --- /dev/null +++ b/packages/EasterEgg/res/values-night/q_colors.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright 2019 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<resources> + <color name="pixel_off">#000000</color> + <color name="pixel_on">#FFFFFF</color> + + <color name="q_clue_bg">@color/navy</color> + <color name="q_clue_text">@color/tan</color> +</resources>
\ No newline at end of file diff --git a/packages/EasterEgg/res/values/q_colors.xml b/packages/EasterEgg/res/values/q_colors.xml new file mode 100644 index 000000000000..5e92c84fd97d --- /dev/null +++ b/packages/EasterEgg/res/values/q_colors.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright 2019 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<resources> + <color name="emerald">#3ddc84</color> + <color name="red">#f8c734</color> + <color name="navy">#073042</color> + <color name="vapor">#d7effe</color> + <color name="tan">#eff7cf</color> + + <color name="pixel_off">#FFFFFF</color> + <color name="pixel_on">#000000</color> + + <color name="q_clue_bg">@color/tan</color> + <color name="q_clue_text">@color/navy</color> + <color name="q_clue_bg_correct">@color/emerald</color> + + <color name="q_icon_fg">@color/emerald</color> +</resources> diff --git a/packages/EasterEgg/res/values/q_puzzles.xml b/packages/EasterEgg/res/values/q_puzzles.xml new file mode 100644 index 000000000000..7c2eff152ffe --- /dev/null +++ b/packages/EasterEgg/res/values/q_puzzles.xml @@ -0,0 +1,214 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string-array name="puzzles"> + + <item>q</item> + <item>q</item> + <item>q</item> + <item>q</item> + <item>q</item> + + <item>android:drawable/ic_info</item> + + <item>android:drawable/stat_sys_adb</item> + <item>android:drawable/stat_sys_battery</item> + <item>android:drawable/stat_sys_phone_call</item> + <item>android:drawable/stat_sys_certificate_info</item> + <item>android:drawable/stat_sys_data_bluetooth</item> + <item>android:drawable/stat_sys_data_usb</item> + <item>android:drawable/stat_sys_download</item> + <item>android:drawable/stat_sys_gps_on</item> + <item>android:drawable/stat_sys_phone_call</item> + <item>android:drawable/stat_sys_tether_wifi</item> + <item>android:drawable/stat_sys_throttled</item> + <item>android:drawable/stat_sys_upload</item> + + <item>android:drawable/stat_notify_car_mode</item> + <item>android:drawable/stat_notify_chat</item> + <item>android:drawable/stat_notify_disk_full</item> + <item>android:drawable/stat_notify_email_generic</item> + <item>android:drawable/stat_notify_error</item> + <item>android:drawable/stat_notify_gmail</item> + <item>android:drawable/stat_notify_missed_call</item> + <item>android:drawable/stat_notify_mmcc_indication_icn</item> + <item>android:drawable/stat_notify_more</item> + <item>android:drawable/stat_notify_rssi_in_range</item> + <item>android:drawable/stat_notify_sdcard</item> + <item>android:drawable/stat_notify_sdcard_prepare</item> + <item>android:drawable/stat_notify_sdcard_usb</item> + <item>android:drawable/stat_notify_sim_toolkit</item> + <item>android:drawable/stat_notify_sync</item> + <item>android:drawable/stat_notify_sync_anim0</item> + <item>android:drawable/stat_notify_sync_error</item> + <item>android:drawable/stat_notify_voicemail</item> + + <item>android:drawable/ic_audio_alarm</item> + <item>android:drawable/ic_audio_alarm_mute</item> + <item>android:drawable/ic_bluetooth_share_icon</item> + <item>android:drawable/ic_bt_headphones_a2dp</item> + <item>android:drawable/ic_bt_headset_hfp</item> + <item>android:drawable/ic_bt_hearing_aid</item> + <item>android:drawable/ic_bt_laptop</item> + <item>android:drawable/ic_bt_misc_hid</item> + <item>android:drawable/ic_bt_network_pan</item> + <item>android:drawable/ic_bt_pointing_hid</item> + <item>android:drawable/ic_corp_badge</item> + <item>android:drawable/ic_expand_more</item> + <item>android:drawable/ic_faster_emergency</item> + <item>android:drawable/ic_file_copy</item> + <item>android:drawable/ic_info_outline_24</item> + <item>android:drawable/ic_lock</item> + <item>android:drawable/ic_lock_bugreport</item> + <item>android:drawable/ic_lock_open</item> + <item>android:drawable/ic_lock_power_off</item> + <item>android:drawable/ic_lockscreen_ime</item> + <item>android:drawable/ic_mode_edit</item> + <item>android:drawable/ic_phone</item> + <item>android:drawable/ic_qs_airplane</item> + <item>android:drawable/ic_qs_auto_rotate</item> + <item>android:drawable/ic_qs_battery_saver</item> + <item>android:drawable/ic_qs_bluetooth</item> + <item>android:drawable/ic_qs_dnd</item> + <item>android:drawable/ic_qs_flashlight</item> + <item>android:drawable/ic_qs_night_display_on</item> + <item>android:drawable/ic_restart</item> + <item>android:drawable/ic_screenshot</item> + <item>android:drawable/ic_settings_bluetooth</item> + <item>android:drawable/ic_signal_cellular_0_4_bar</item> + <item>android:drawable/ic_signal_cellular_0_5_bar</item> + <item>android:drawable/ic_signal_cellular_1_4_bar</item> + <item>android:drawable/ic_signal_cellular_1_5_bar</item> + <item>android:drawable/ic_signal_cellular_2_4_bar</item> + <item>android:drawable/ic_signal_cellular_2_5_bar</item> + <item>android:drawable/ic_signal_cellular_3_4_bar</item> + <item>android:drawable/ic_signal_cellular_3_5_bar</item> + <item>android:drawable/ic_signal_cellular_4_4_bar</item> + <item>android:drawable/ic_signal_cellular_4_5_bar</item> + <item>android:drawable/ic_signal_cellular_5_5_bar</item> + <item>android:drawable/ic_signal_location</item> + <item>android:drawable/ic_wifi_signal_0</item> + <item>android:drawable/ic_wifi_signal_1</item> + <item>android:drawable/ic_wifi_signal_2</item> + <item>android:drawable/ic_wifi_signal_3</item> + <item>android:drawable/ic_wifi_signal_4</item> + <item>android:drawable/perm_group_activity_recognition</item> + <item>android:drawable/perm_group_calendar</item> + <item>android:drawable/perm_group_call_log</item> + <item>android:drawable/perm_group_camera</item> + <item>android:drawable/perm_group_contacts</item> + <item>android:drawable/perm_group_location</item> + <item>android:drawable/perm_group_microphone</item> + <item>android:drawable/perm_group_phone_calls</item> + <item>android:drawable/perm_group_sensors</item> + <item>android:drawable/perm_group_sms</item> + <item>android:drawable/perm_group_storage</item> + <item>android:drawable/perm_group_visual</item> + + <item>com.android.settings:drawable/ic_add_24dp</item> + <item>com.android.settings:drawable/ic_airplanemode_active</item> + <item>com.android.settings:drawable/ic_android</item> + <item>com.android.settings:drawable/ic_apps</item> + <item>com.android.settings:drawable/ic_arrow_back</item> + <item>com.android.settings:drawable/ic_arrow_down_24dp</item> + <item>com.android.settings:drawable/ic_battery_charging_full</item> + <item>com.android.settings:drawable/ic_battery_status_bad_24dp</item> + <item>com.android.settings:drawable/ic_battery_status_good_24dp</item> + <item>com.android.settings:drawable/ic_battery_status_maybe_24dp</item> + <item>com.android.settings:drawable/ic_call_24dp</item> + <item>com.android.settings:drawable/ic_cancel</item> + <item>com.android.settings:drawable/ic_cast_24dp</item> + <item>com.android.settings:drawable/ic_chevron_right_24dp</item> + <item>com.android.settings:drawable/ic_data_saver</item> + <item>com.android.settings:drawable/ic_delete</item> + <item>com.android.settings:drawable/ic_devices_other</item> + <item>com.android.settings:drawable/ic_devices_other_opaque_black</item> + <item>com.android.settings:drawable/ic_do_not_disturb_on_24dp</item> + <item>com.android.settings:drawable/ic_eject_24dp</item> + <item>com.android.settings:drawable/ic_expand_less</item> + <item>com.android.settings:drawable/ic_expand_more_inverse</item> + <item>com.android.settings:drawable/ic_folder_vd_theme_24</item> + <item>com.android.settings:drawable/ic_friction_lock_closed</item> + <item>com.android.settings:drawable/ic_gray_scale_24dp</item> + <item>com.android.settings:drawable/ic_headset_24dp</item> + <item>com.android.settings:drawable/ic_help</item> + <item>com.android.settings:drawable/ic_local_movies</item> + <item>com.android.settings:drawable/ic_lock</item> + <item>com.android.settings:drawable/ic_media_stream</item> + <item>com.android.settings:drawable/ic_network_cell</item> + <item>com.android.settings:drawable/ic_notifications</item> + <item>com.android.settings:drawable/ic_notifications_off_24dp</item> + <item>com.android.settings:drawable/ic_phone_info</item> + <item>com.android.settings:drawable/ic_photo_library</item> + <item>com.android.settings:drawable/ic_settings_accessibility</item> + <item>com.android.settings:drawable/ic_settings_accounts</item> + <item>com.android.settings:drawable/ic_settings_backup</item> + <item>com.android.settings:drawable/ic_settings_battery_white</item> + <item>com.android.settings:drawable/ic_settings_data_usage</item> + <item>com.android.settings:drawable/ic_settings_date_time</item> + <item>com.android.settings:drawable/ic_settings_delete</item> + <item>com.android.settings:drawable/ic_settings_display_white</item> + <item>com.android.settings:drawable/ic_settings_home</item> + <item>com.android.settings:drawable/ic_settings_location</item> + <item>com.android.settings:drawable/ic_settings_night_display</item> + <item>com.android.settings:drawable/ic_settings_open</item> + <item>com.android.settings:drawable/ic_settings_print</item> + <item>com.android.settings:drawable/ic_settings_privacy</item> + <item>com.android.settings:drawable/ic_settings_security_white</item> + <item>com.android.settings:drawable/ic_settings_sim</item> + <item>com.android.settings:drawable/ic_settings_wireless</item> + <item>com.android.settings:drawable/ic_storage</item> + <item>com.android.settings:drawable/ic_storage_white</item> + <item>com.android.settings:drawable/ic_suggestion_night_display</item> + <item>com.android.settings:drawable/ic_sync</item> + <item>com.android.settings:drawable/ic_system_update</item> + <item>com.android.settings:drawable/ic_videogame_vd_theme_24</item> + <item>com.android.settings:drawable/ic_volume_ringer_vibrate</item> + <item>com.android.settings:drawable/ic_volume_up_24dp</item> + <item>com.android.settings:drawable/ic_vpn_key</item> + <item>com.android.settings:drawable/ic_wifi_tethering</item> + + <item>com.android.systemui:drawable/ic_alarm</item> + <item>com.android.systemui:drawable/ic_alarm_dim</item> + <item>com.android.systemui:drawable/ic_arrow_back</item> + <item>com.android.systemui:drawable/ic_bluetooth_connected</item> + <item>com.android.systemui:drawable/ic_brightness_thumb</item> + <item>com.android.systemui:drawable/ic_camera</item> + <item>com.android.systemui:drawable/ic_cast</item> + <item>com.android.systemui:drawable/ic_cast_connected</item> + <item>com.android.systemui:drawable/ic_cast_connected_fill</item> + <item>com.android.systemui:drawable/ic_close_white</item> + <item>com.android.systemui:drawable/ic_data_saver</item> + <item>com.android.systemui:drawable/ic_data_saver_off</item> + <item>com.android.systemui:drawable/ic_drag_handle</item> + <item>com.android.systemui:drawable/ic_headset</item> + <item>com.android.systemui:drawable/ic_headset_mic</item> + <item>com.android.systemui:drawable/ic_hotspot</item> + <item>com.android.systemui:drawable/ic_invert_colors</item> + <item>com.android.systemui:drawable/ic_location</item> + <item>com.android.systemui:drawable/ic_lockscreen_ime</item> + <item>com.android.systemui:drawable/ic_notifications_alert</item> + <item>com.android.systemui:drawable/ic_notifications_silence</item> + <item>com.android.systemui:drawable/ic_power_low</item> + <item>com.android.systemui:drawable/ic_power_saver</item> + <item>com.android.systemui:drawable/ic_qs_bluetooth_connecting</item> + <item>com.android.systemui:drawable/ic_qs_bluetooth_on</item> + <item>com.android.systemui:drawable/ic_qs_cancel</item> + <item>com.android.systemui:drawable/ic_qs_no_sim</item> + <item>com.android.systemui:drawable/ic_screenshot_delete</item> + <item>com.android.systemui:drawable/ic_settings</item> + <item>com.android.systemui:drawable/ic_swap_vert</item> + <item>com.android.systemui:drawable/ic_volume_alarm</item> + <item>com.android.systemui:drawable/ic_volume_alarm_mute</item> + <item>com.android.systemui:drawable/ic_volume_media</item> + <item>com.android.systemui:drawable/ic_volume_media_mute</item> + <item>com.android.systemui:drawable/ic_volume_ringer</item> + <item>com.android.systemui:drawable/ic_volume_ringer_mute</item> + <item>com.android.systemui:drawable/ic_volume_ringer_vibrate</item> + <item>com.android.systemui:drawable/ic_volume_voice</item> + <item>com.android.systemui:drawable/stat_sys_camera</item> + <item>com.android.systemui:drawable/stat_sys_managed_profile_status</item> + <item>com.android.systemui:drawable/stat_sys_mic_none</item> + <item>com.android.systemui:drawable/stat_sys_vpn_ic</item> + + </string-array> +</resources> diff --git a/packages/EasterEgg/res/values/strings.xml b/packages/EasterEgg/res/values/strings.xml index 32dbc97a00fb..b95ec6be4c84 100644 --- a/packages/EasterEgg/res/values/strings.xml +++ b/packages/EasterEgg/res/values/strings.xml @@ -1,5 +1,4 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- +<?xml version="1.0" encoding="utf-8"?><!-- Copyright (C) 2018 The Android Open Source Project Licensed under the Apache License, Version 2.0 (the "License"); @@ -15,5 +14,11 @@ Copyright (C) 2018 The Android Open Source Project limitations under the License. --> <resources xmlns:android="http://schemas.android.com/apk/res/android"> - <string name="app_name" translatable="false">PAINT.APK</string> + <string name="app_name" translatable="false">Android Q Easter Egg</string> + + <!-- name of the Q easter egg, a nonogram-style icon puzzle --> + <string name="q_egg_name" translatable="false">Icon Quiz</string> + + <!-- name of the P easter egg, a humble paint program --> + <string name="p_egg_name" translatable="false">PAINT.APK</string> </resources> diff --git a/packages/EasterEgg/res/values/styles.xml b/packages/EasterEgg/res/values/styles.xml index 44e2ce52aab8..e576526f49b7 100644 --- a/packages/EasterEgg/res/values/styles.xml +++ b/packages/EasterEgg/res/values/styles.xml @@ -20,4 +20,16 @@ <item name="android:windowLightNavigationBar">true</item> </style> + <style name="QuaresTheme" parent="@android:style/Theme.DeviceDefault.DayNight"> + <item name="android:windowIsTranslucent">true</item> + <item name="android:windowBackground">@android:color/transparent</item> + <item name="android:colorBackgroundCacheHint">@null</item> + <item name="android:windowShowWallpaper">true</item> + <item name="android:windowContentOverlay">@null</item> + <item name="android:windowNoTitle">true</item> + <item name="android:windowFullscreen">true</item> + <item name="android:statusBarColor">@android:color/transparent</item> + <item name="android:navigationBarColor">@android:color/transparent</item> + </style> + </resources> diff --git a/packages/EasterEgg/src/com/android/egg/quares/Quare.kt b/packages/EasterEgg/src/com/android/egg/quares/Quare.kt new file mode 100644 index 000000000000..eb77362a0be2 --- /dev/null +++ b/packages/EasterEgg/src/com/android/egg/quares/Quare.kt @@ -0,0 +1,168 @@ +/* + * Copyright 2019 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.egg.quares + +import android.content.Context +import android.graphics.Bitmap +import android.graphics.Canvas +import android.graphics.drawable.Drawable +import android.graphics.drawable.Icon +import android.os.Parcel +import android.os.Parcelable +import java.util.ArrayList +import kotlin.math.abs +import kotlin.math.round + +class Quare(val width: Int, val height: Int, val depth: Int) : Parcelable { + private val data: IntArray = IntArray(width * height) + private val user: IntArray = data.copyOf() + + private fun loadAndQuantize(bitmap8bpp: Bitmap) { + bitmap8bpp.getPixels(data, 0, width, 0, 0, width, height) + if (depth == 8) return + val s = (255f / depth) + for (i in 0 until data.size) { + var f = (data[i] ushr 24).toFloat() / s + // f = f.pow(0.75f) // gamma adjust for bolder lines + f *= 1.25f // brightness adjust for bolder lines + f.coerceAtMost(1f) + data[i] = (round(f) * s).toInt() shl 24 + } + } + + fun isBlank(): Boolean { + return data.sum() == 0 + } + + fun load(drawable: Drawable) { + val resized = Bitmap.createBitmap(width, height, Bitmap.Config.ALPHA_8) + val canvas = Canvas(resized) + drawable.setBounds(0, 0, width, height) + drawable.setTint(0xFF000000.toInt()) + drawable.draw(canvas) + loadAndQuantize(resized) + resized.recycle() + } + + fun load(context: Context, icon: Icon) { + icon.loadDrawable(context)?.let { + load(it) + } + } + + fun bitmap(): Bitmap { + return Bitmap.createBitmap(data, width, height, Bitmap.Config.ALPHA_8) + } + + fun getUserMark(x: Int, y: Int): Int { + return user[y * width + x] ushr 24 + } + + fun setUserMark(x: Int, y: Int, v: Int) { + user[y * width + x] = v shl 24 + } + + fun getDataAt(x: Int, y: Int): Int { + return data[y * width + x] ushr 24 + } + + fun check(): Boolean { + return data.contentEquals(user) + } + + fun check(xSel: Int, ySel: Int): Boolean { + val xStart = if (xSel < 0) 0 else xSel + val xEnd = if (xSel < 0) width - 1 else xSel + val yStart = if (ySel < 0) 0 else ySel + val yEnd = if (ySel < 0) height - 1 else ySel + for (y in yStart..yEnd) + for (x in xStart..xEnd) + if (getDataAt(x, y) != getUserMark(x, y)) return false + return true + } + + fun errors(): IntArray { + return IntArray(width * height) { + abs(data[it] - user[it]) + } + } + + fun getRowClue(y: Int): IntArray { + return getClue(-1, y) + } + fun getColumnClue(x: Int): IntArray { + return getClue(x, -1) + } + fun getClue(xSel: Int, ySel: Int): IntArray { + val arr = ArrayList<Int>() + var len = 0 + val xStart = if (xSel < 0) 0 else xSel + val xEnd = if (xSel < 0) width - 1 else xSel + val yStart = if (ySel < 0) 0 else ySel + val yEnd = if (ySel < 0) height - 1 else ySel + for (y in yStart..yEnd) + for (x in xStart..xEnd) + if (getDataAt(x, y) != 0) { + len++ + } else if (len > 0) { + arr.add(len) + len = 0 + } + if (len > 0) arr.add(len) + else if (arr.size == 0) arr.add(0) + return arr.toIntArray() + } + + fun resetUserMarks() { + user.forEachIndexed { index, _ -> user[index] = 0 } + } + + // Parcelable interface + + override fun describeContents(): Int { + return 0 + } + + override fun writeToParcel(p: Parcel?, flags: Int) { + p?.let { + p.writeInt(width) + p.writeInt(height) + p.writeInt(depth) + p.writeIntArray(data) + p.writeIntArray(user) + } + } + + companion object CREATOR : Parcelable.Creator<Quare> { + override fun createFromParcel(p: Parcel?): Quare { + return p!!.let { + Quare( + p.readInt(), // width + p.readInt(), // height + p.readInt() // depth + ).also { + p.readIntArray(it.data) + p.readIntArray(it.user) + } + } + } + + override fun newArray(size: Int): Array<Quare?> { + return arrayOfNulls(size) + } + } +} diff --git a/packages/EasterEgg/src/com/android/egg/quares/QuaresActivity.kt b/packages/EasterEgg/src/com/android/egg/quares/QuaresActivity.kt new file mode 100644 index 000000000000..ce439a9a663c --- /dev/null +++ b/packages/EasterEgg/src/com/android/egg/quares/QuaresActivity.kt @@ -0,0 +1,312 @@ +/* + * Copyright 2019 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.egg.quares + +import android.app.Activity +import android.content.Context +import android.content.res.Configuration +import android.graphics.Canvas +import android.graphics.Paint +import android.graphics.Typeface +import android.graphics.drawable.Icon +import android.os.Bundle +import android.text.StaticLayout +import android.text.TextPaint +import android.util.Log +import android.view.View +import android.view.View.GONE +import android.view.View.VISIBLE +import android.widget.Button +import android.widget.CompoundButton +import android.widget.GridLayout + +import java.util.Random + +import com.android.egg.R + +const val TAG = "Quares" + +class QuaresActivity : Activity() { + private var q: Quare = Quare(16, 16, 1) + private var resId = 0 + private var resName = "" + private var icon: Icon? = null + + private lateinit var label: Button + private lateinit var grid: GridLayout + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + window.decorView.systemUiVisibility = + View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or View.SYSTEM_UI_FLAG_LAYOUT_STABLE + + actionBar?.hide() + + setContentView(R.layout.activity_quares) + + grid = findViewById(R.id.grid) + label = findViewById(R.id.label) + + if (savedInstanceState != null) { + Log.v(TAG, "restoring puzzle from state") + q = savedInstanceState.getParcelable("q") ?: q + resId = savedInstanceState.getInt("resId") + resName = savedInstanceState.getString("resName", "") + loadPuzzle() + } + + label.setOnClickListener { newPuzzle() } + } + + override fun onResume() { + super.onResume() + if (resId == 0) { + // lazy init from onCreate + newPuzzle() + } + checkVictory() + } + + override fun onSaveInstanceState(outState: Bundle) { + super.onSaveInstanceState(outState) + + outState.putParcelable("q", q) + outState.putInt("resId", resId) + outState.putString("resName", resName) + } + + fun newPuzzle() { + Log.v(TAG, "new puzzle...") + + q.resetUserMarks() + val oldResId = resId + resId = android.R.drawable.stat_sys_warning + try { + for (tries in 0..3) { + val ar = resources.obtainTypedArray(R.array.puzzles) + val newName = ar.getString(Random().nextInt(ar.length())) + if (newName == null) continue + + Log.v(TAG, "Looking for icon " + newName) + + val pkg = getPackageNameForResourceName(newName) + val newId = packageManager.getResourcesForApplication(pkg) + .getIdentifier(newName, "drawable", pkg) + if (newId == 0) { + Log.v(TAG, "oops, " + newName + " doesn't resolve from pkg " + pkg) + } else if (newId != oldResId) { + // got a good one + resId = newId + resName = newName + break + } + } + } catch (e: RuntimeException) { + Log.v(TAG, "problem loading puzzle, using fallback", e) + } + loadPuzzle() + } + + fun getPackageNameForResourceName(name: String): String { + return if (name.contains(":") && !name.startsWith("android:")) { + name.substring(0, name.indexOf(":")) + } else { + packageName + } + } + + fun checkVictory() { + if (q.check()) { + val dp = resources.displayMetrics.density + + val label: Button = findViewById(R.id.label) + label.text = resName.replace(Regex("^.*/"), "") + val drawable = icon?.loadDrawable(this)?.also { + it.setBounds(0, 0, (32 * dp).toInt(), (32 * dp).toInt()) + it.setTint(label.currentTextColor) + } + label.setCompoundDrawables(drawable, null, null, null) + + label.visibility = VISIBLE + } else { + label.visibility = GONE + } + } + + fun loadPuzzle() { + Log.v(TAG, "loading " + resName + " at " + q.width + "x" + q.height) + + val dp = resources.displayMetrics.density + + icon = Icon.createWithResource(getPackageNameForResourceName(resName), resId) + q.load(this, icon!!) + + if (q.isBlank()) { + // this is a really boring puzzle, let's try again + resId = 0 + resName = "" + recreate() + return + } + + grid.removeAllViews() + grid.columnCount = q.width + 1 + grid.rowCount = q.height + 1 + + label.visibility = GONE + + val orientation = resources.configuration.orientation + + // clean this up a bit + val minSide = resources.configuration.smallestScreenWidthDp - 25 // ish + val size = (minSide / (q.height + 0.5) * dp).toInt() + + val sb = StringBuffer() + + for (j in 0 until grid.rowCount) { + for (i in 0 until grid.columnCount) { + val tv: View + val params = GridLayout.LayoutParams().also { + it.width = size + it.height = size + it.setMargins(1, 1, 1, 1) + it.rowSpec = GridLayout.spec(GridLayout.UNDEFINED, GridLayout.TOP) // UGH + } + val x = i - 1 + val y = j - 1 + if (i > 0 && j > 0) { + if (i == 1 && j > 1) sb.append("\n") + sb.append(if (q.getDataAt(x, y) == 0) " " else "X") + tv = PixelButton(this) + tv.isChecked = q.getUserMark(x, y) != 0 + tv.setOnClickListener { + q.setUserMark(x, y, if (tv.isChecked) 0xFF else 0) + val columnCorrect = (grid.getChildAt(i) as? ClueView)?.check(q) ?: false + val rowCorrect = (grid.getChildAt(j*(grid.columnCount)) as? ClueView) + ?.check(q) ?: false + if (columnCorrect && rowCorrect) { + checkVictory() + } else { + label.visibility = GONE + } + } + } else if (i == j) { // 0,0 + tv = View(this) + tv.visibility = GONE + } else { + tv = ClueView(this) + if (j == 0) { + tv.textRotation = 90f + if (orientation == Configuration.ORIENTATION_LANDSCAPE) { + params.height /= 2 + tv.showText = false + } else { + params.height = (96 * dp).toInt() + } + if (x >= 0) { + tv.setColumn(q, x) + } + } + if (i == 0) { + if (orientation == Configuration.ORIENTATION_PORTRAIT) { + params.width /= 2 + tv.showText = false + } else { + params.width = (96 * dp).toInt() + } + if (y >= 0) { + tv.setRow(q, y) + } + } + } + grid.addView(tv, params) + } + } + + Log.v(TAG, "icon: \n" + sb) + } +} + +class PixelButton(context: Context) : CompoundButton(context) { + init { + setBackgroundResource(R.drawable.pixel_bg) + isClickable = true + isEnabled = true + } +} + +class ClueView(context: Context) : View(context) { + var row: Int = -1 + var column: Int = -1 + var textRotation: Float = 0f + var text: CharSequence = "" + var showText = true + val paint: TextPaint + val incorrectColor: Int + val correctColor: Int + + init { + setBackgroundColor(0) + paint = TextPaint().also { + it.textSize = 14f * context.resources.displayMetrics.density + it.color = context.getColor(R.color.q_clue_text) + it.typeface = Typeface.DEFAULT_BOLD + it.textAlign = Paint.Align.CENTER + } + incorrectColor = context.getColor(R.color.q_clue_bg) + correctColor = context.getColor(R.color.q_clue_bg_correct) + } + + fun setRow(q: Quare, row: Int): Boolean { + this.row = row + this.column = -1 + this.textRotation = 0f + text = q.getRowClue(row).joinToString("-") + return check(q) + } + fun setColumn(q: Quare, column: Int): Boolean { + this.column = column + this.row = -1 + this.textRotation = 90f + text = q.getColumnClue(column).joinToString("-") + return check(q) + } + fun check(q: Quare): Boolean { + val correct = q.check(column, row) + setBackgroundColor(if (correct) correctColor else incorrectColor) + return correct + } + + override fun onDraw(canvas: Canvas?) { + super.onDraw(canvas) + if (!showText) return + canvas?.let { + val x = canvas.width / 2f + val y = canvas.height / 2f + var textWidth = canvas.width + if (textRotation != 0f) { + canvas.rotate(textRotation, x, y) + textWidth = canvas.height + } + val textLayout = StaticLayout.Builder.obtain( + text, 0, text.length, paint, textWidth).build() + canvas.translate(x, y - textLayout.height / 2) + textLayout.draw(canvas) + } + } +} diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java index 7f906f6c5b06..6d874ab2be9b 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java @@ -117,7 +117,7 @@ public class HidProfile implements LocalBluetoothProfile { public boolean isPreferred(BluetoothDevice device) { if (mService == null) return false; - return mService.getPriority(device) > BluetoothProfile.PRIORITY_OFF; + return mService.getPriority(device) != BluetoothProfile.PRIORITY_OFF; } public int getPreferred(BluetoothDevice device) { diff --git a/packages/SettingsLib/src/com/android/settingslib/net/DataUsageUtils.java b/packages/SettingsLib/src/com/android/settingslib/net/DataUsageUtils.java index de38e8a366b4..b15ea980ae15 100644 --- a/packages/SettingsLib/src/com/android/settingslib/net/DataUsageUtils.java +++ b/packages/SettingsLib/src/com/android/settingslib/net/DataUsageUtils.java @@ -18,15 +18,11 @@ package com.android.settingslib.net; import android.content.Context; import android.net.NetworkTemplate; -import android.os.ParcelUuid; import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; import android.util.Log; -import java.util.ArrayList; -import java.util.List; - /** * Utils class for data usage */ @@ -38,7 +34,7 @@ public class DataUsageUtils { */ public static NetworkTemplate getMobileTemplate(Context context, int subId) { final TelephonyManager telephonyManager = context.getSystemService( - TelephonyManager.class); + TelephonyManager.class).createForSubscriptionId(subId); final SubscriptionManager subscriptionManager = context.getSystemService( SubscriptionManager.class); final SubscriptionInfo info = subscriptionManager.getActiveSubscriptionInfo(subId); @@ -49,23 +45,8 @@ public class DataUsageUtils { Log.i(TAG, "Subscription is not active: " + subId); return mobileAll; } - final ParcelUuid groupUuid = info.getGroupUuid(); - if (groupUuid == null) { - Log.i(TAG, "Subscription doesn't have valid group uuid: " + subId); - return mobileAll; - } - // Otherwise merge other subscriberId to create new NetworkTemplate - final List<SubscriptionInfo> groupInfos = subscriptionManager.getSubscriptionsInGroup( - groupUuid); - final List<String> mergedSubscriberIds = new ArrayList<>(); - for (SubscriptionInfo subInfo : groupInfos) { - final String subscriberId = telephonyManager.getSubscriberId( - subInfo.getSubscriptionId()); - if (subscriberId != null) { - mergedSubscriberIds.add(subscriberId); - } - } - return NetworkTemplate.normalize(mobileAll, mergedSubscriberIds.toArray(new String[0])); + // Use old API to build networkTemplate + return NetworkTemplate.normalize(mobileAll, telephonyManager.getMergedSubscriberIds()); } } diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/DataUsageUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/DataUsageUtilsTest.java index dc33cfe4b2f8..821c0b3a23f7 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/DataUsageUtilsTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/DataUsageUtilsTest.java @@ -44,7 +44,6 @@ import java.util.List; public class DataUsageUtilsTest { private static final int SUB_ID = 1; - private static final int SUB_ID_2 = 2; private static final String SUBSCRIBER_ID = "Test Subscriber"; private static final String SUBSCRIBER_ID_2 = "Test Subscriber 2"; @@ -67,11 +66,11 @@ public class DataUsageUtilsTest { mContext = spy(RuntimeEnvironment.application); when(mContext.getSystemService(TelephonyManager.class)).thenReturn(mTelephonyManager); + when(mTelephonyManager.createForSubscriptionId(SUB_ID)).thenReturn(mTelephonyManager); when(mContext.getSystemService(SubscriptionManager.class)).thenReturn(mSubscriptionManager); when(mTelephonyManager.getSubscriberId(SUB_ID)).thenReturn(SUBSCRIBER_ID); - when(mTelephonyManager.getSubscriberId(SUB_ID_2)).thenReturn(SUBSCRIBER_ID_2); - when(mInfo1.getSubscriptionId()).thenReturn(SUB_ID); - when(mInfo2.getSubscriptionId()).thenReturn(SUB_ID_2); + when(mTelephonyManager.getMergedSubscriberIds()).thenReturn( + new String[]{SUBSCRIBER_ID, SUBSCRIBER_ID_2}); mInfos = new ArrayList<>(); mInfos.add(mInfo1); @@ -89,17 +88,7 @@ public class DataUsageUtilsTest { } @Test - public void getMobileTemplate_groupUuidNull_returnMobileAll() { - when(mSubscriptionManager.getActiveSubscriptionInfo(SUB_ID)).thenReturn(mInfo1); - when(mInfo1.getGroupUuid()).thenReturn(null); - - final NetworkTemplate networkTemplate = DataUsageUtils.getMobileTemplate(mContext, SUB_ID); - assertThat(networkTemplate.matchesSubscriberId(SUBSCRIBER_ID)).isTrue(); - assertThat(networkTemplate.matchesSubscriberId(SUBSCRIBER_ID_2)).isFalse(); - } - - @Test - public void getMobileTemplate_groupUuidExist_returnMobileMerged() { + public void getMobileTemplate_infoExisted_returnMobileMerged() { when(mSubscriptionManager.getActiveSubscriptionInfo(SUB_ID)).thenReturn(mInfo1); when(mInfo1.getGroupUuid()).thenReturn(mParcelUuid); diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index 19e7b734912a..6e6c009dd18c 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -112,6 +112,9 @@ wifi,bt,dnd,flashlight,rotation,battery,cell,airplane,cast </string> + <!-- The minimum number of tiles to display in QuickSettings --> + <integer name="quick_settings_min_num_tiles">6</integer> + <!-- Tiles native to System UI. Order should match "quick_settings_tiles_default" --> <string name="quick_settings_tiles_stock" translatable="false"> wifi,cell,battery,dnd,flashlight,rotation,bt,airplane,location,hotspot,inversion,saver,dark,work,cast,night diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 8199ea346807..591af82ecb4e 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -2009,7 +2009,7 @@ <string name="drag_to_remove_tiles">Drag here to remove</string> <!-- Label to indicate to users that additional tiles cannot be removed. [CHAR LIMIT=60] --> - <string name="drag_to_remove_disabled">You need at least 6 tiles</string> + <string name="drag_to_remove_disabled">You need at least <xliff:g id="min_num_tiles" example="6">%1$d</xliff:g> tiles</string> <!-- Button to edit the tile ordering of quick settings [CHAR LIMIT=60] --> <string name="qs_edit">Edit</string> diff --git a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java index 9a0e9fc92af6..c9d4957494e4 100644 --- a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java +++ b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java @@ -46,6 +46,8 @@ public class ImageWallpaper extends WallpaperService { // We delayed destroy render context that subsequent render requests have chance to cancel it. // This is to avoid destroying then recreating render context in a very short time. private static final int DELAY_FINISH_RENDERING = 1000; + private static final int INTERVAL_WAIT_FOR_RENDERING = 100; + private static final int PATIENCE_WAIT_FOR_RENDERING = 5; private HandlerThread mWorker; @Override @@ -80,7 +82,10 @@ public class ImageWallpaper extends WallpaperService { private StatusBarStateController mController; private final Runnable mFinishRenderingTask = this::finishRendering; private final boolean mNeedTransition; + private final Object mMonitor = new Object(); private boolean mNeedRedraw; + // This variable can only be accessed in synchronized block. + private boolean mWaitingForRendering; GLEngine(Context context) { mNeedTransition = ActivityManager.isHighEndGfx() @@ -122,6 +127,27 @@ public class ImageWallpaper extends WallpaperService { long duration = mNeedTransition || animationDuration != 0 ? animationDuration : 0; mWorker.getThreadHandler().post( () -> mRenderer.updateAmbientMode(inAmbientMode, duration)); + if (inAmbientMode && duration == 0) { + // This means that we are transiting from home to aod, to avoid + // race condition between window visibility and transition, + // we don't return until the transition is finished. See b/136643341. + waitForBackgroundRendering(); + } + } + + private void waitForBackgroundRendering() { + synchronized (mMonitor) { + try { + mWaitingForRendering = true; + for (int patience = 1; mWaitingForRendering; patience++) { + mMonitor.wait(INTERVAL_WAIT_FOR_RENDERING); + mWaitingForRendering &= patience < PATIENCE_WAIT_FOR_RENDERING; + } + } catch (InterruptedException ex) { + } finally { + mWaitingForRendering = false; + } + } } @Override @@ -178,7 +204,8 @@ public class ImageWallpaper extends WallpaperService { @Override public void preRender() { - mWorker.getThreadHandler().post(this::preRenderInternal); + // This method should only be invoked from worker thread. + preRenderInternal(); } private void preRenderInternal() { @@ -212,7 +239,8 @@ public class ImageWallpaper extends WallpaperService { @Override public void requestRender() { - mWorker.getThreadHandler().post(this::requestRenderInternal); + // This method should only be invoked from worker thread. + requestRenderInternal(); } private void requestRenderInternal() { @@ -234,7 +262,21 @@ public class ImageWallpaper extends WallpaperService { @Override public void postRender() { - mWorker.getThreadHandler().post(this::scheduleFinishRendering); + // This method should only be invoked from worker thread. + notifyWaitingThread(); + scheduleFinishRendering(); + } + + private void notifyWaitingThread() { + synchronized (mMonitor) { + if (mWaitingForRendering) { + try { + mWaitingForRendering = false; + mMonitor.notify(); + } catch (IllegalMonitorStateException ex) { + } + } + } } private void cancelFinishRenderingTask() { diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageRevealHelper.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageRevealHelper.java index 6a1f24afe620..45e97b38d87e 100644 --- a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageRevealHelper.java +++ b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageRevealHelper.java @@ -84,7 +84,17 @@ class ImageRevealHelper { void updateAwake(boolean awake, long duration) { mAwake = awake; mAnimator.setDuration(duration); - animate(); + if (!mAwake && duration == 0) { + // We are transiting from home to aod, + // since main thread is waiting for rendering finished, we only need draw + // the last state directly, which is a black screen. + mReveal = MIN_REVEAL; + mRevealListener.onRevealStart(); + mRevealListener.onRevealStateChanged(); + mRevealListener.onRevealEnd(); + } else { + animate(); + } } /** diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java index 8ed5424cf673..2542abdbef72 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java +++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java @@ -19,6 +19,7 @@ import android.app.AlertDialog.Builder; import android.content.ComponentName; import android.content.Context; import android.content.DialogInterface; +import android.content.res.Resources; import android.graphics.Canvas; import android.graphics.drawable.Drawable; import android.os.Handler; @@ -54,7 +55,6 @@ import java.util.ArrayList; import java.util.List; public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileStateListener { - private static final int MIN_NUM_TILES = 6; private static final long DRAG_LENGTH = 100; private static final float DRAG_SCALE = 1.2f; public static final long MOVE_DURATION = 150; @@ -79,6 +79,7 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta private final ItemTouchHelper mItemTouchHelper; private final ItemDecoration mDecoration; private final AccessibilityManager mAccessibilityManager; + private final int mMinNumTiles; private int mEditIndex; private int mTileDividerIndex; private boolean mNeedsFocus; @@ -97,6 +98,7 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta mAccessibilityManager = context.getSystemService(AccessibilityManager.class); mItemTouchHelper = new ItemTouchHelper(mCallbacks); mDecoration = new TileItemDecoration(context); + mMinNumTiles = context.getResources().getInteger(R.integer.quick_settings_min_num_tiles); } public void setHost(QSTileHost host) { @@ -247,15 +249,17 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta return; } if (holder.getItemViewType() == TYPE_EDIT) { - final int titleResId; + final String titleText; + Resources res = mContext.getResources(); if (mCurrentDrag == null) { - titleResId = R.string.drag_to_add_tiles; + titleText = res.getString(R.string.drag_to_add_tiles); } else if (!canRemoveTiles() && mCurrentDrag.getAdapterPosition() < mEditIndex) { - titleResId = R.string.drag_to_remove_disabled; + titleText = res.getString(R.string.drag_to_remove_disabled, mMinNumTiles); } else { - titleResId = R.string.drag_to_remove_tiles; + titleText = res.getString(R.string.drag_to_remove_tiles); } - ((TextView) holder.itemView.findViewById(android.R.id.title)).setText(titleResId); + + ((TextView) holder.itemView.findViewById(android.R.id.title)).setText(titleText); return; } if (holder.getItemViewType() == TYPE_ACCESSIBLE_DROP) { @@ -337,7 +341,7 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta } private boolean canRemoveTiles() { - return mCurrentSpecs.size() > MIN_NUM_TILES; + return mCurrentSpecs.size() > mMinNumTiles; } private void selectPosition(int position, View v) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java index b70b45b9db84..404920187351 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java @@ -16,8 +16,11 @@ package com.android.systemui.statusbar; +import static com.android.systemui.Dependency.MAIN_HANDLER_NAME; + import android.content.Context; import android.content.res.Resources; +import android.os.Handler; import android.os.Trace; import android.os.UserHandle; import android.util.Log; @@ -43,6 +46,7 @@ import java.util.List; import java.util.Stack; import javax.inject.Inject; +import javax.inject.Named; import javax.inject.Singleton; import dagger.Lazy; @@ -58,6 +62,8 @@ import dagger.Lazy; public class NotificationViewHierarchyManager implements DynamicPrivacyController.Listener { private static final String TAG = "NotificationViewHierarchyManager"; + private final Handler mHandler; + //TODO: change this top <Entry, List<Entry>>? private final HashMap<ExpandableNotificationRow, List<ExpandableNotificationRow>> mTmpChildOrderMap = new HashMap<>(); @@ -86,9 +92,13 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle // Used to help track down re-entrant calls to our update methods, which will cause bugs. private boolean mPerformingUpdate; + // Hack to get around re-entrant call in onDynamicPrivacyChanged() until we can track down + // the problem. + private boolean mIsHandleDynamicPrivacyChangeScheduled; @Inject public NotificationViewHierarchyManager(Context context, + @Named(MAIN_HANDLER_NAME) Handler mainHandler, NotificationLockscreenUserManager notificationLockscreenUserManager, NotificationGroupManager groupManager, VisualStabilityManager visualStabilityManager, @@ -97,6 +107,7 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle Lazy<ShadeController> shadeController, BubbleData bubbleData, DynamicPrivacyController privacyController) { + mHandler = mainHandler; mLockscreenUserManager = notificationLockscreenUserManager; mGroupManager = groupManager; mVisualStabilityManager = visualStabilityManager; @@ -435,19 +446,33 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle @Override public void onDynamicPrivacyChanged() { + if (mPerformingUpdate) { + Log.w(TAG, "onDynamicPrivacyChanged made a re-entrant call"); + } + // This listener can be called from updateNotificationViews() via a convoluted listener + // chain, so we post here to prevent a re-entrant call. See b/136186188 + // TODO: Refactor away the need for this + if (!mIsHandleDynamicPrivacyChangeScheduled) { + mIsHandleDynamicPrivacyChangeScheduled = true; + mHandler.post(this::onHandleDynamicPrivacyChanged); + } + } + + private void onHandleDynamicPrivacyChanged() { + mIsHandleDynamicPrivacyChangeScheduled = false; updateNotificationViews(); } private void beginUpdate() { if (mPerformingUpdate) { - throw new IllegalStateException("Re-entrant code during update."); + Log.wtf(TAG, "Re-entrant code during update", new Exception()); } mPerformingUpdate = true; } private void endUpdate() { if (!mPerformingUpdate) { - throw new IllegalStateException("Manager state has become desynced."); + Log.wtf(TAG, "Manager state has become desynced", new Exception()); } mPerformingUpdate = false; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java index a0cda693822f..1d0d231424d1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java @@ -22,6 +22,7 @@ import android.content.Context; import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.Rect; +import android.graphics.Region; import android.util.Log; import android.util.Pools; import android.view.DisplayCutout; @@ -76,7 +77,7 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable, private int[] mTmpTwoArray = new int[2]; private boolean mHeadsUpGoingAway; private int mStatusBarState; - private Rect mTouchableRegion = new Rect(); + private Region mTouchableRegion = new Region(); private AnimationStateHandler mAnimationStateHandler; @@ -299,10 +300,11 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable, info.touchableRegion.set(calculateTouchableRegion()); } - public Rect calculateTouchableRegion() { + public Region calculateTouchableRegion() { if (!hasPinnedHeadsUp()) { mTouchableRegion.set(0, 0, mStatusBarWindowView.getWidth(), mStatusBarHeight); updateRegionForNotch(mTouchableRegion); + } else { NotificationEntry topEntry = getTopEntry(); if (topEntry.isChildInGroup()) { @@ -322,7 +324,7 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable, return mTouchableRegion; } - private void updateRegionForNotch(Rect region) { + private void updateRegionForNotch(Region region) { DisplayCutout cutout = mStatusBarWindowView.getRootWindowInsets().getDisplayCutout(); if (cutout == null) { return; @@ -390,6 +392,8 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable, super.dumpInternal(fd, pw, args); pw.print(" mBarState="); pw.println(mStatusBarState); + pw.print(" mTouchableRegion="); + pw.println(mTouchableRegion); } /////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java index 1027046b0c28..a423adfd34b7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java @@ -38,6 +38,7 @@ import android.graphics.PointF; import android.graphics.PorterDuff; import android.graphics.PorterDuffXfermode; import android.graphics.Rect; +import android.graphics.Region; import android.os.PowerManager; import android.util.AttributeSet; import android.util.Log; @@ -620,9 +621,10 @@ public class NotificationPanelView extends PanelView implements private Rect calculateGestureExclusionRect() { Rect exclusionRect = null; - if (isFullyCollapsed()) { + Region touchableRegion = mHeadsUpManager.calculateTouchableRegion(); + if (isFullyCollapsed() && touchableRegion != null) { // Note: The heads up manager also calculates the non-pinned touchable region - exclusionRect = mHeadsUpManager.calculateTouchableRegion(); + exclusionRect = touchableRegion.getBounds(); } return exclusionRect != null ? exclusionRect diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java index c476d802c4e5..5103e8e89209 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java @@ -20,12 +20,16 @@ import static junit.framework.Assert.assertTrue; import static org.junit.Assert.assertEquals; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.clearInvocations; +import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.os.Handler; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.view.View; @@ -78,13 +82,19 @@ public class NotificationViewHierarchyManagerTest extends SysuiTestCase { @Mock private VisualStabilityManager mVisualStabilityManager; @Mock private ShadeController mShadeController; + private TestableLooper mTestableLooper; + private Handler mHandler; private NotificationViewHierarchyManager mViewHierarchyManager; private NotificationTestHelper mHelper; + private boolean mMadeReentrantCall = false; @Before public void setUp() { MockitoAnnotations.initMocks(this); - Assert.sMainLooper = TestableLooper.get(this).getLooper(); + mTestableLooper = TestableLooper.get(this); + Assert.sMainLooper = mTestableLooper.getLooper(); + mHandler = Handler.createAsync(mTestableLooper.getLooper()); + mDependency.injectTestDependency(NotificationEntryManager.class, mEntryManager); mDependency.injectTestDependency(NotificationLockscreenUserManager.class, mLockscreenUserManager); @@ -97,7 +107,7 @@ public class NotificationViewHierarchyManagerTest extends SysuiTestCase { when(mEntryManager.getNotificationData()).thenReturn(mNotificationData); mViewHierarchyManager = new NotificationViewHierarchyManager(mContext, - mLockscreenUserManager, mGroupManager, mVisualStabilityManager, + mHandler, mLockscreenUserManager, mGroupManager, mVisualStabilityManager, mock(StatusBarStateControllerImpl.class), mEntryManager, () -> mShadeController, new BubbleData(mContext), mock(DynamicPrivacyController.class)); Dependency.get(InitController.class).executePostInitTasks(); @@ -212,9 +222,60 @@ public class NotificationViewHierarchyManagerTest extends SysuiTestCase { verify(entry0.getRow(), times(1)).showAppOpsIcons(any()); } + @Test + public void testReentrantCallsToOnDynamicPrivacyChangedPostForLater() { + // GIVEN a ListContainer that will make a re-entrant call to updateNotificationViews() + mMadeReentrantCall = false; + doAnswer((invocation) -> { + if (!mMadeReentrantCall) { + mMadeReentrantCall = true; + mViewHierarchyManager.onDynamicPrivacyChanged(); + } + return null; + }).when(mListContainer).setMaxDisplayedNotifications(anyInt()); + + // WHEN we call updateNotificationViews() + mViewHierarchyManager.updateNotificationViews(); + + // THEN onNotificationViewUpdateFinished() is only called once + verify(mListContainer).onNotificationViewUpdateFinished(); + + // WHEN we drain the looper + mTestableLooper.processAllMessages(); + + // THEN updateNotificationViews() is called a second time (for the reentrant call) + verify(mListContainer, times(2)).onNotificationViewUpdateFinished(); + } + + @Test + public void testMultipleReentrantCallsToOnDynamicPrivacyChangedOnlyPostOnce() { + // GIVEN a ListContainer that will make many re-entrant calls to updateNotificationViews() + mMadeReentrantCall = false; + doAnswer((invocation) -> { + if (!mMadeReentrantCall) { + mMadeReentrantCall = true; + mViewHierarchyManager.onDynamicPrivacyChanged(); + mViewHierarchyManager.onDynamicPrivacyChanged(); + mViewHierarchyManager.onDynamicPrivacyChanged(); + mViewHierarchyManager.onDynamicPrivacyChanged(); + } + return null; + }).when(mListContainer).setMaxDisplayedNotifications(anyInt()); + + // WHEN we call updateNotificationViews() and drain the looper + mViewHierarchyManager.updateNotificationViews(); + verify(mListContainer).onNotificationViewUpdateFinished(); + clearInvocations(mListContainer); + mTestableLooper.processAllMessages(); + + // THEN updateNotificationViews() is called only one more time + verify(mListContainer).onNotificationViewUpdateFinished(); + } + private class FakeListContainer implements NotificationListContainer { final LinearLayout mLayout = new LinearLayout(mContext); final List<View> mRows = Lists.newArrayList(); + private boolean mMakeReentrantCallDuringSetMaxDisplayedNotifications; @Override public void setChildTransferInProgress(boolean childTransferInProgress) {} @@ -263,7 +324,11 @@ public class NotificationViewHierarchyManagerTest extends SysuiTestCase { } @Override - public void setMaxDisplayedNotifications(int maxNotifications) {} + public void setMaxDisplayedNotifications(int maxNotifications) { + if (mMakeReentrantCallDuringSetMaxDisplayedNotifications) { + mViewHierarchyManager.onDynamicPrivacyChanged(); + } + } @Override public ViewGroup getViewParentForNotification(NotificationEntry entry) { @@ -298,5 +363,7 @@ public class NotificationViewHierarchyManagerTest extends SysuiTestCase { return false; } + @Override + public void onNotificationViewUpdateFinished() { } } } diff --git a/services/core/java/com/android/server/UiModeManagerService.java b/services/core/java/com/android/server/UiModeManagerService.java index bc7da3fe2edc..30a356325ada 100644 --- a/services/core/java/com/android/server/UiModeManagerService.java +++ b/services/core/java/com/android/server/UiModeManagerService.java @@ -413,15 +413,15 @@ final class UiModeManagerService extends SystemService { try { synchronized (mLock) { if (mNightMode != mode) { - if (UserManager.get(getContext()).isPrimaryUser()) { - SystemProperties.set(SYSTEM_PROPERTY_DEVICE_THEME, - Integer.toString(mode)); - } - // Only persist setting if not in car mode if (!mCarModeEnabled) { Secure.putIntForUser(getContext().getContentResolver(), Secure.UI_NIGHT_MODE, mode, user); + + if (UserManager.get(getContext()).isPrimaryUser()) { + SystemProperties.set(SYSTEM_PROPERTY_DEVICE_THEME, + Integer.toString(mode)); + } } mNightMode = mode; 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 a7da3ec950f4..d3e5df5d62d1 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java @@ -2269,6 +2269,11 @@ public class PermissionManagerService { return; } + // Permission is already revoked, no need to do anything. + if (!permissionsState.hasRuntimePermission(permName, userId)) { + return; + } + if (permissionsState.revokeRuntimePermission(bp, userId) == PERMISSION_OPERATION_FAILURE) { return; diff --git a/services/core/java/com/android/server/policy/PermissionPolicyService.java b/services/core/java/com/android/server/policy/PermissionPolicyService.java index 037293f9536c..a569bffef141 100644 --- a/services/core/java/com/android/server/policy/PermissionPolicyService.java +++ b/services/core/java/com/android/server/policy/PermissionPolicyService.java @@ -798,7 +798,7 @@ public final class PermissionPolicyService extends SystemService { @Override public boolean checkStartActivity(@NonNull Intent intent, int callingUid, @Nullable String callingPackage) { - if (callingPackage != null && isActionRemovedForCallingPackage(intent.getAction(), + if (callingPackage != null && isActionRemovedForCallingPackage(intent, callingUid, callingPackage)) { Slog.w(LOG_TAG, "Action Removed: starting " + intent.toString() + " from " + callingPackage + " (uid=" + callingUid + ")"); @@ -811,8 +811,9 @@ public final class PermissionPolicyService extends SystemService { * Check if the intent action is removed for the calling package (often based on target SDK * version). If the action is removed, we'll silently cancel the activity launch. */ - private boolean isActionRemovedForCallingPackage(@Nullable String action, + private boolean isActionRemovedForCallingPackage(@NonNull Intent intent, int callingUid, @NonNull String callingPackage) { + String action = intent.getAction(); if (action == null) { return false; } @@ -821,15 +822,19 @@ public final class PermissionPolicyService extends SystemService { case Telephony.Sms.Intents.ACTION_CHANGE_DEFAULT: { ApplicationInfo applicationInfo; try { - applicationInfo = getContext().getPackageManager().getApplicationInfo( - callingPackage, 0); + applicationInfo = getContext().getPackageManager().getApplicationInfoAsUser( + callingPackage, 0, UserHandle.getUserId(callingUid)); + if (applicationInfo.targetSdkVersion >= Build.VERSION_CODES.Q) { + // Applications targeting Q or higher should use + // RoleManager.createRequestRoleIntent() instead. + return true; + } } catch (PackageManager.NameNotFoundException e) { Slog.i(LOG_TAG, "Cannot find application info for " + callingPackage); - return false; } - // Applications targeting Q should use RoleManager.createRequestRoleIntent() - // instead. - return applicationInfo.targetSdkVersion >= Build.VERSION_CODES.Q; + // Make sure RequestRoleActivity can know the calling package if we allow it. + intent.putExtra(Intent.EXTRA_CALLING_PACKAGE, callingPackage); + return false; } default: return false; diff --git a/services/core/java/com/android/server/role/RoleManagerService.java b/services/core/java/com/android/server/role/RoleManagerService.java index 9cd6b0d32793..c6a1867fa1e9 100644 --- a/services/core/java/com/android/server/role/RoleManagerService.java +++ b/services/core/java/com/android/server/role/RoleManagerService.java @@ -49,6 +49,7 @@ import android.os.ResultReceiver; import android.os.ShellCallback; import android.os.UserHandle; import android.os.UserManagerInternal; +import android.provider.Telephony; import android.service.sms.FinancialSmsService; import android.telephony.IFinancialSmsCallback; import android.text.TextUtils; @@ -60,6 +61,7 @@ import android.util.SparseArray; import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.GuardedBy; +import com.android.internal.telephony.SmsApplication; import com.android.internal.util.ArrayUtils; import com.android.internal.util.BitUtils; import com.android.internal.util.CollectionUtils; @@ -377,13 +379,16 @@ public class RoleManagerService extends SystemService implements RoleUserState.C } @Override - public void onRoleHoldersChanged(@NonNull String roleName, @UserIdInt int userId) { + public void onRoleHoldersChanged(@NonNull String roleName, @UserIdInt int userId, + @Nullable String removedHolder, @Nullable String addedHolder) { mListenerHandler.sendMessage(PooledLambda.obtainMessage( - RoleManagerService::notifyRoleHoldersChanged, this, roleName, userId)); + RoleManagerService::notifyRoleHoldersChanged, this, roleName, userId, + removedHolder, addedHolder)); } @WorkerThread - private void notifyRoleHoldersChanged(@NonNull String roleName, @UserIdInt int userId) { + private void notifyRoleHoldersChanged(@NonNull String roleName, @UserIdInt int userId, + @Nullable String removedHolder, @Nullable String addedHolder) { RemoteCallbackList<IOnRoleHoldersChangedListener> listeners = getListeners(userId); if (listeners != null) { notifyRoleHoldersChangedForListeners(listeners, roleName, userId); @@ -394,6 +399,12 @@ public class RoleManagerService extends SystemService implements RoleUserState.C if (allUsersListeners != null) { notifyRoleHoldersChangedForListeners(allUsersListeners, roleName, userId); } + + // Legacy: sms app changed broadcasts + if (RoleManager.ROLE_SMS.equals(roleName)) { + SmsApplication.broadcastSmsAppChange(getContext(), UserHandle.of(userId), + removedHolder, addedHolder); + } } @WorkerThread diff --git a/services/core/java/com/android/server/role/RoleUserState.java b/services/core/java/com/android/server/role/RoleUserState.java index c7e3fa4f2074..6375b4851283 100644 --- a/services/core/java/com/android/server/role/RoleUserState.java +++ b/services/core/java/com/android/server/role/RoleUserState.java @@ -294,7 +294,7 @@ public class RoleUserState { } if (changed) { - mCallback.onRoleHoldersChanged(roleName, mUserId); + mCallback.onRoleHoldersChanged(roleName, mUserId, null, packageName); } return true; } @@ -328,7 +328,7 @@ public class RoleUserState { } if (changed) { - mCallback.onRoleHoldersChanged(roleName, mUserId); + mCallback.onRoleHoldersChanged(roleName, mUserId, packageName, null); } return true; } @@ -632,6 +632,7 @@ public class RoleUserState { * @param roleName the name of the role whose holders are changed * @param userId the user id for this role holder change */ - void onRoleHoldersChanged(@NonNull String roleName, @UserIdInt int userId); + void onRoleHoldersChanged(@NonNull String roleName, @UserIdInt int userId, + @Nullable String removedHolder, @Nullable String addedHolder); } } diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java index e107c9aedf38..08c1bb536211 100644 --- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java +++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java @@ -86,9 +86,9 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { private static final String TAG = "RollbackManager"; - // Rollbacks expire after 48 hours. + // Rollbacks expire after 14 days. private static final long DEFAULT_ROLLBACK_LIFETIME_DURATION_MILLIS = - TimeUnit.HOURS.toMillis(48); + TimeUnit.DAYS.toMillis(14); // Lock used to synchronize accesses to in-memory rollback data // structures. By convention, methods with the suffix "Locked" require @@ -1289,7 +1289,8 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { private boolean packageVersionsEqual(VersionedPackage a, VersionedPackage b) { - return a.getPackageName().equals(b.getPackageName()) + return a != null && b != null + && a.getPackageName().equals(b.getPackageName()) && a.getLongVersionCode() == b.getLongVersionCode(); } diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java index 9908b3657121..b0f1e5d69be4 100644 --- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java +++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java @@ -1172,8 +1172,16 @@ public class WallpaperManagerService extends IWallpaperManager.Stub return false; } final int displayId = display.getDisplayId(); - return displayId == DEFAULT_DISPLAY - || mWindowManagerInternal.shouldShowSystemDecorOnDisplay(displayId); + if (displayId == DEFAULT_DISPLAY) { + return true; + } + + final long ident = Binder.clearCallingIdentity(); + try { + return mWindowManagerInternal.shouldShowSystemDecorOnDisplay(displayId); + } finally { + Binder.restoreCallingIdentity(ident); + } } void forEachDisplayConnector(Consumer<DisplayConnector> action) { diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 0faea61b9d60..371a94356432 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -1616,8 +1616,11 @@ final class ActivityRecord extends ConfigurationContainer { try { ArrayList<ReferrerIntent> ar = new ArrayList<>(1); ar.add(rintent); + // Making sure the client state is RESUMED after transaction completed and doing + // so only if activity is currently RESUMED. Otherwise, client may have extra + // life-cycle calls to RESUMED (and PAUSED later). mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), appToken, - NewIntentItem.obtain(ar)); + NewIntentItem.obtain(ar, mState == RESUMED)); unsent = false; } catch (RemoteException e) { Slog.w(TAG, "Exception thrown sending new intent to " + this, e); diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java index 30e866b8e2a4..6bed46226b42 100644 --- a/services/core/java/com/android/server/wm/ActivityStack.java +++ b/services/core/java/com/android/server/wm/ActivityStack.java @@ -2964,7 +2964,8 @@ class ActivityStack extends ConfigurationContainer { } if (next.newIntents != null) { - transaction.addCallback(NewIntentItem.obtain(next.newIntents)); + transaction.addCallback( + NewIntentItem.obtain(next.newIntents, true /* resume */)); } // Well the app will no longer be stopped. diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java index c992a69c2ecb..19916bc617f4 100644 --- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java @@ -415,7 +415,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { void sendErrorResult(String message) { try { - if (callerApp.hasThread()) { + if (callerApp != null && callerApp.hasThread()) { callerApp.getThread().scheduleCrash(message); } } catch (RemoteException e) { diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 1659131d880d..b837d9ec874b 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -140,7 +140,7 @@ import static com.android.server.wm.WindowManagerService.logSurface; import static com.android.server.wm.WindowState.RESIZE_HANDLE_WIDTH_IN_DP; import static com.android.server.wm.WindowStateAnimator.DRAW_PENDING; import static com.android.server.wm.WindowStateAnimator.READY_TO_SHOW; -import static com.android.server.wm.utils.RegionUtils.forEachRect; +import static com.android.server.wm.utils.RegionUtils.forEachRectReverse; import static com.android.server.wm.utils.RegionUtils.rectListToRegion; import android.animation.AnimationHandler; @@ -5142,7 +5142,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo final int[] remainingLeftRight = {mSystemGestureExclusionLimit, mSystemGestureExclusionLimit}; - // Traverse all windows bottom up to assemble the gesture exclusion rects. + // Traverse all windows top down to assemble the gesture exclusion rects. // For each window, we only take the rects that fall within its touchable region. forAllWindows(w -> { if (w.cantReceiveTouchInput() || !w.isVisible() @@ -5150,23 +5150,25 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo || unhandled.isEmpty()) { return; } - final boolean modal = - (w.mAttrs.flags & (FLAG_NOT_TOUCH_MODAL | FLAG_NOT_FOCUSABLE)) == 0; // Get the touchable region of the window, and intersect with where the screen is still // touchable, i.e. touchable regions on top are not covering it yet. - w.getTouchableRegion(touchableRegion); + w.getEffectiveTouchableRegion(touchableRegion); touchableRegion.op(unhandled, Op.INTERSECT); - rectListToRegion(w.getSystemGestureExclusion(), local); + if (w.isImplicitlyExcludingAllSystemGestures()) { + local.set(touchableRegion); + } else { + rectListToRegion(w.getSystemGestureExclusion(), local); - // Transform to display coordinates - local.scale(w.mGlobalScale); - final Rect frame = w.getWindowFrames().mFrame; - local.translate(frame.left, frame.top); + // Transform to display coordinates + local.scale(w.mGlobalScale); + final Rect frame = w.getWindowFrames().mFrame; + local.translate(frame.left, frame.top); - // A window can only exclude system gestures where it is actually touchable - local.op(touchableRegion, Op.INTERSECT); + // A window can only exclude system gestures where it is actually touchable + local.op(touchableRegion, Op.INTERSECT); + } // Apply restriction if necessary. if (needsGestureExclusionRestrictions(w, mLastDispatchedSystemUiVisibility)) { @@ -5225,13 +5227,13 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo r.op(edge, Op.INTERSECT); final int[] remaining = {limit}; - forEachRect(r, rect -> { + forEachRectReverse(r, rect -> { if (remaining[0] <= 0) { return; } final int height = rect.height(); if (height > remaining[0]) { - rect.bottom = rect.top + remaining[0]; + rect.top = rect.bottom - remaining[0]; } remaining[0] -= height; global.op(rect, Op.UNION); diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java index 6127303141f4..553b0ffa6999 100644 --- a/services/core/java/com/android/server/wm/DragState.java +++ b/services/core/java/com/android/server/wm/DragState.java @@ -176,6 +176,8 @@ class DragState { mTransaction.transferTouchFocus(mTransferTouchFromToken, h.token); mTransferTouchFromToken = null; + // syncInputWindows here to ensure the input channel isn't removed before the transfer. + mTransaction.syncInputWindows(); mTransaction.apply(); } diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 2db0131a122f..fb57d73c9a21 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -32,6 +32,7 @@ import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.os.Process.SYSTEM_UID; import static android.os.Process.myPid; import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; +import static android.provider.DeviceConfig.WindowManager.KEY_SYSTEM_GESTURES_EXCLUDED_BY_PRE_Q_STICKY_IMMERSIVE; import static android.provider.DeviceConfig.WindowManager.KEY_SYSTEM_GESTURE_EXCLUSION_LIMIT_DP; import static android.provider.Settings.Global.DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS; import static android.view.Display.DEFAULT_DISPLAY; @@ -844,6 +845,7 @@ public class WindowManagerService extends IWindowManager.Stub boolean mWindowsChanged = false; int mSystemGestureExclusionLimitDp; + boolean mSystemGestureExcludedByPreQStickyImmersive; public interface WindowChangeListener { public void windowsChanged(); @@ -1142,13 +1144,21 @@ public class WindowManagerService extends IWindowManager.Stub mSystemGestureExclusionLimitDp = Math.max(MIN_GESTURE_EXCLUSION_LIMIT_DP, DeviceConfig.getInt(DeviceConfig.NAMESPACE_WINDOW_MANAGER, KEY_SYSTEM_GESTURE_EXCLUSION_LIMIT_DP, 0)); + mSystemGestureExcludedByPreQStickyImmersive = + DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_WINDOW_MANAGER, + KEY_SYSTEM_GESTURES_EXCLUDED_BY_PRE_Q_STICKY_IMMERSIVE, false); DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_WINDOW_MANAGER, new HandlerExecutor(mH), properties -> { synchronized (mGlobalLock) { final int exclusionLimitDp = Math.max(MIN_GESTURE_EXCLUSION_LIMIT_DP, properties.getInt(KEY_SYSTEM_GESTURE_EXCLUSION_LIMIT_DP, 0)); - if (mSystemGestureExclusionLimitDp != exclusionLimitDp) { + final boolean excludedByPreQSticky = DeviceConfig.getBoolean( + DeviceConfig.NAMESPACE_WINDOW_MANAGER, + KEY_SYSTEM_GESTURES_EXCLUDED_BY_PRE_Q_STICKY_IMMERSIVE, false); + if (mSystemGestureExcludedByPreQStickyImmersive != excludedByPreQSticky + || mSystemGestureExclusionLimitDp != exclusionLimitDp) { mSystemGestureExclusionLimitDp = exclusionLimitDp; + mSystemGestureExcludedByPreQStickyImmersive = excludedByPreQSticky; mRoot.forAllDisplays(DisplayContent::updateSystemGestureExclusionLimit); } } diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index c6c9e1b39db4..43ad091b08c0 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -24,6 +24,8 @@ import static android.os.PowerManager.DRAW_WAKE_LOCK; import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.SurfaceControl.Transaction; +import static android.view.View.SYSTEM_UI_FLAG_HIDE_NAVIGATION; +import static android.view.View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY; import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT; import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME; import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION; @@ -155,6 +157,7 @@ import android.graphics.Point; import android.graphics.Rect; import android.graphics.Region; import android.os.Binder; +import android.os.Build; import android.os.Debug; import android.os.IBinder; import android.os.PowerManager; @@ -657,6 +660,15 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP return true; } + boolean isImplicitlyExcludingAllSystemGestures() { + final int immersiveStickyFlags = + SYSTEM_UI_FLAG_HIDE_NAVIGATION | SYSTEM_UI_FLAG_IMMERSIVE_STICKY; + final boolean immersiveSticky = + (mSystemUiVisibility & immersiveStickyFlags) == immersiveStickyFlags; + return immersiveSticky && mWmService.mSystemGestureExcludedByPreQStickyImmersive + && mAppToken != null && mAppToken.mTargetSdk < Build.VERSION_CODES.Q; + } + interface PowerManagerWrapper { void wakeUp(long time, @WakeReason int reason, String details); @@ -2999,6 +3011,25 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP subtractTouchExcludeRegionIfNeeded(outRegion); } + /** + * Get the effective touchable region in global coordinates. + * + * In contrast to {@link #getTouchableRegion}, this takes into account + * {@link WindowManager.LayoutParams#FLAG_NOT_TOUCH_MODAL touch modality.} + */ + void getEffectiveTouchableRegion(Region outRegion) { + final boolean modal = (mAttrs.flags & (FLAG_NOT_TOUCH_MODAL | FLAG_NOT_FOCUSABLE)) == 0; + final DisplayContent dc = getDisplayContent(); + + if (modal && dc != null) { + outRegion.set(dc.getBounds()); + cropRegionToStackBoundsIfNeeded(outRegion); + subtractTouchExcludeRegionIfNeeded(outRegion); + } else { + getTouchableRegion(outRegion); + } + } + private void setTouchableRegionCropIfNeeded(InputWindowHandle handle) { final Task task = getTask(); if (task == null || !task.cropWindowsToStackBounds()) { diff --git a/services/core/java/com/android/server/wm/utils/RegionUtils.java b/services/core/java/com/android/server/wm/utils/RegionUtils.java index 8cd6f8826083..b1b30701df4f 100644 --- a/services/core/java/com/android/server/wm/utils/RegionUtils.java +++ b/services/core/java/com/android/server/wm/utils/RegionUtils.java @@ -20,6 +20,8 @@ import android.graphics.Rect; import android.graphics.Region; import android.graphics.RegionIterator; +import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.function.Consumer; @@ -48,14 +50,21 @@ public class RegionUtils { /** * Applies actions on each rect contained within a {@code Region}. * + * Order is bottom to top, then right to left. + * * @param region the given region. * @param rectConsumer the action holder. */ - public static void forEachRect(Region region, Consumer<Rect> rectConsumer) { + public static void forEachRectReverse(Region region, Consumer<Rect> rectConsumer) { final RegionIterator it = new RegionIterator(region); + final ArrayList<Rect> rects = new ArrayList<>(); final Rect rect = new Rect(); while (it.next(rect)) { - rectConsumer.accept(rect); + rects.add(new Rect(rect)); } + // TODO: instead of creating an array and reversing it, expose the reverse iterator through + // JNI. + Collections.reverse(rects); + rects.forEach(rectConsumer); } } diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java index f49a57534938..7cd097ebdc2b 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java @@ -26,8 +26,13 @@ import static android.view.Display.DEFAULT_DISPLAY; import static android.view.DisplayCutout.BOUNDS_POSITION_LEFT; import static android.view.DisplayCutout.BOUNDS_POSITION_TOP; import static android.view.DisplayCutout.fromBoundingRect; +import static android.view.View.SYSTEM_UI_FLAG_FULLSCREEN; +import static android.view.View.SYSTEM_UI_FLAG_HIDE_NAVIGATION; +import static android.view.View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY; import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR; import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN; +import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES; +import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; @@ -789,6 +794,58 @@ public class DisplayContentTests extends WindowTestsBase { } @Test + public void testCalculateSystemGestureExclusion_modal() throws Exception { + final DisplayContent dc = createNewDisplay(); + final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, dc, "base"); + win.getAttrs().flags |= FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR; + win.setSystemGestureExclusion(Collections.singletonList(new Rect(0, 0, 1000, 1000))); + + final WindowState win2 = createWindow(null, TYPE_APPLICATION, dc, "modal"); + win2.getAttrs().flags |= FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR; + win2.getAttrs().privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION; + win2.getAttrs().width = 10; + win2.getAttrs().height = 10; + win2.setSystemGestureExclusion(Collections.emptyList()); + + dc.setLayoutNeeded(); + dc.performLayout(true /* initial */, false /* updateImeWindows */); + + win.setHasSurface(true); + win2.setHasSurface(true); + + final Region expected = Region.obtain(); + assertEquals(expected, dc.calculateSystemGestureExclusion()); + } + + @Test + public void testCalculateSystemGestureExclusion_immersiveStickyLegacyWindow() throws Exception { + synchronized (mWm.mGlobalLock) { + mWm.mSystemGestureExcludedByPreQStickyImmersive = true; + + final DisplayContent dc = createNewDisplay(); + final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, dc, "win"); + win.getAttrs().flags |= FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR; + win.getAttrs().layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES; + win.getAttrs().privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION; + win.getAttrs().subtreeSystemUiVisibility = win.mSystemUiVisibility = + SYSTEM_UI_FLAG_FULLSCREEN | SYSTEM_UI_FLAG_HIDE_NAVIGATION + | SYSTEM_UI_FLAG_IMMERSIVE_STICKY; + win.mAppToken.mTargetSdk = P; + + dc.setLayoutNeeded(); + dc.performLayout(true /* initial */, false /* updateImeWindows */); + + win.setHasSurface(true); + + final Region expected = Region.obtain(); + expected.set(dc.getBounds()); + assertEquals(expected, dc.calculateSystemGestureExclusion()); + + win.setHasSurface(false); + } + } + + @Test public void testOrientationChangeLogging() { MetricsLogger mockLogger = mock(MetricsLogger.class); Configuration oldConfig = new Configuration(); diff --git a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java index c3561f4bf6ab..d034f274f1e1 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java +++ b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java @@ -49,6 +49,7 @@ class TestWindowManagerPolicy implements WindowManagerPolicy { int mRotationToReport = 0; boolean mKeyguardShowingAndNotOccluded = false; + boolean mOkToAnimate = true; private Runnable mRunnableWhenAddingSplashScreen; @@ -222,7 +223,7 @@ class TestWindowManagerPolicy implements WindowManagerPolicy { @Override public boolean okToAnimate() { - return true; + return mOkToAnimate; } @Override diff --git a/telephony/java/com/android/internal/telephony/SmsApplication.java b/telephony/java/com/android/internal/telephony/SmsApplication.java index 44dc24bf716e..98f52cbf93da 100644 --- a/telephony/java/com/android/internal/telephony/SmsApplication.java +++ b/telephony/java/com/android/internal/telephony/SmsApplication.java @@ -17,6 +17,7 @@ package com.android.internal.telephony; import android.Manifest.permission; +import android.annotation.Nullable; import android.app.AppOpsManager; import android.app.role.RoleManager; import android.content.ComponentName; @@ -662,49 +663,69 @@ public final class SmsApplication { } defaultSmsAppChanged(context); + } + } + + /** + * Sends broadcasts on sms app change: + * {@link Intent#ACTION_DEFAULT_SMS_PACKAGE_CHANGED} + * {@link Intents.ACTION_DEFAULT_SMS_PACKAGE_CHANGED_INTERNAL} + */ + public static void broadcastSmsAppChange(Context context, + UserHandle userHandle, @Nullable String oldPackage, @Nullable String newPackage) { + Collection<SmsApplicationData> apps = getApplicationCollection(context); + broadcastSmsAppChange(context, userHandle, + getApplicationForPackage(apps, oldPackage), + getApplicationForPackage(apps, newPackage)); + } + + private static void broadcastSmsAppChange(Context context, UserHandle userHandle, + @Nullable SmsApplicationData oldAppData, + @Nullable SmsApplicationData applicationData) { + if (DEBUG_MULTIUSER) { + Log.i(LOG_TAG, "setDefaultApplicationInternal oldAppData=" + oldAppData); + } + if (oldAppData != null && oldAppData.mSmsAppChangedReceiverClass != null) { + // Notify the old sms app that it's no longer the default + final Intent oldAppIntent = + new Intent(Intents.ACTION_DEFAULT_SMS_PACKAGE_CHANGED); + final ComponentName component = new ComponentName(oldAppData.mPackageName, + oldAppData.mSmsAppChangedReceiverClass); + oldAppIntent.setComponent(component); + oldAppIntent.putExtra(Intents.EXTRA_IS_DEFAULT_SMS_APP, false); if (DEBUG_MULTIUSER) { - Log.i(LOG_TAG, "setDefaultApplicationInternal oldAppData=" + oldAppData); - } - if (oldAppData != null && oldAppData.mSmsAppChangedReceiverClass != null) { - // Notify the old sms app that it's no longer the default - final Intent oldAppIntent = - new Intent(Telephony.Sms.Intents.ACTION_DEFAULT_SMS_PACKAGE_CHANGED); - final ComponentName component = new ComponentName(oldAppData.mPackageName, - oldAppData.mSmsAppChangedReceiverClass); - oldAppIntent.setComponent(component); - oldAppIntent.putExtra(Telephony.Sms.Intents.EXTRA_IS_DEFAULT_SMS_APP, false); - if (DEBUG_MULTIUSER) { - Log.i(LOG_TAG, "setDefaultApplicationInternal old=" + oldAppData.mPackageName); - } - context.sendBroadcastAsUser(oldAppIntent, userHandle); + Log.i(LOG_TAG, "setDefaultApplicationInternal old=" + oldAppData.mPackageName); } - // Notify the new sms app that it's now the default (if the new sms app has a receiver - // to handle the changed default sms intent). + context.sendBroadcastAsUser(oldAppIntent, userHandle); + } + // Notify the new sms app that it's now the default (if the new sms app has a receiver + // to handle the changed default sms intent). + if (DEBUG_MULTIUSER) { + Log.i(LOG_TAG, "setDefaultApplicationInternal new applicationData=" + + applicationData); + } + if (applicationData != null && applicationData.mSmsAppChangedReceiverClass != null) { + final Intent intent = + new Intent(Intents.ACTION_DEFAULT_SMS_PACKAGE_CHANGED); + final ComponentName component = new ComponentName(applicationData.mPackageName, + applicationData.mSmsAppChangedReceiverClass); + intent.setComponent(component); + intent.putExtra(Intents.EXTRA_IS_DEFAULT_SMS_APP, true); if (DEBUG_MULTIUSER) { - Log.i(LOG_TAG, "setDefaultApplicationInternal new applicationData=" + - applicationData); - } - if (applicationData.mSmsAppChangedReceiverClass != null) { - final Intent intent = - new Intent(Telephony.Sms.Intents.ACTION_DEFAULT_SMS_PACKAGE_CHANGED); - final ComponentName component = new ComponentName(applicationData.mPackageName, - applicationData.mSmsAppChangedReceiverClass); - intent.setComponent(component); - intent.putExtra(Telephony.Sms.Intents.EXTRA_IS_DEFAULT_SMS_APP, true); - if (DEBUG_MULTIUSER) { - Log.i(LOG_TAG, "setDefaultApplicationInternal new=" + packageName); - } - context.sendBroadcastAsUser(intent, userHandle); + Log.i(LOG_TAG, "setDefaultApplicationInternal new=" + applicationData.mPackageName); } + context.sendBroadcastAsUser(intent, userHandle); + } - // Send an implicit broadcast for the system server. - // (or anyone with MONITOR_DEFAULT_SMS_PACKAGE, really.) - final Intent intent = - new Intent(Telephony.Sms.Intents.ACTION_DEFAULT_SMS_PACKAGE_CHANGED_INTERNAL); - context.sendBroadcastAsUser(intent, userHandle, - permission.MONITOR_DEFAULT_SMS_PACKAGE); + // Send an implicit broadcast for the system server. + // (or anyone with MONITOR_DEFAULT_SMS_PACKAGE, really.) + final Intent intent = + new Intent(Intents.ACTION_DEFAULT_SMS_PACKAGE_CHANGED_INTERNAL); + context.sendBroadcastAsUser(intent, userHandle, + permission.MONITOR_DEFAULT_SMS_PACKAGE); + if (applicationData != null) { MetricsLogger.action(context, MetricsEvent.ACTION_DEFAULT_SMS_APP_CHANGED, applicationData.mPackageName); } diff --git a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java index beff0c6bc308..1b002cadb07f 100644 --- a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java +++ b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java @@ -960,6 +960,7 @@ public class RollbackTest { } @Test + @Ignore("b/136605788") public void testEnableRollbackTimeoutFailsRollback() throws Exception { try { RollbackTestUtils.adoptShellPermissionIdentity( |