diff options
231 files changed, 5393 insertions, 2926 deletions
diff --git a/apct-tests/perftests/core/src/android/database/SQLiteDatabaseIoPerfTest.java b/apct-tests/perftests/core/src/android/database/SQLiteDatabaseIoPerfTest.java new file mode 100644 index 000000000000..7c5316d233a7 --- /dev/null +++ b/apct-tests/perftests/core/src/android/database/SQLiteDatabaseIoPerfTest.java @@ -0,0 +1,176 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package android.database; + +import android.app.Activity; +import android.content.ContentValues; +import android.content.Context; +import android.database.sqlite.SQLiteDatabase; +import android.os.Bundle; +import android.support.test.InstrumentationRegistry; +import android.support.test.filters.LargeTest; +import android.support.test.runner.AndroidJUnit4; +import android.util.ArrayMap; +import android.util.Log; + +import com.android.internal.util.Preconditions; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.util.List; +import java.util.Map; + +import static org.junit.Assert.assertEquals; + +/** + * Performance tests for measuring amount of data written during typical DB operations + * + * <p>To run: bit CorePerfTests:android.database.SQLiteDatabaseIoPerfTest + */ +@RunWith(AndroidJUnit4.class) +@LargeTest +public class SQLiteDatabaseIoPerfTest { + private static final String TAG = "SQLiteDatabaseIoPerfTest"; + private static final String DB_NAME = "db_io_perftest"; + private static final int DEFAULT_DATASET_SIZE = 500; + + private Long mWriteBytes; + + private SQLiteDatabase mDatabase; + private Context mContext; + + @Before + public void setUp() { + mContext = InstrumentationRegistry.getTargetContext(); + mContext.deleteDatabase(DB_NAME); + mDatabase = mContext.openOrCreateDatabase(DB_NAME, Context.MODE_PRIVATE, null); + mDatabase.execSQL("CREATE TABLE T1 " + + "(_ID INTEGER PRIMARY KEY, COL_A INTEGER, COL_B VARCHAR(100), COL_C REAL)"); + } + + @After + public void tearDown() { + mDatabase.close(); + mContext.deleteDatabase(DB_NAME); + } + + @Test + public void testDatabaseModifications() { + startMeasuringWrites(); + ContentValues cv = new ContentValues(); + String[] whereArg = new String[1]; + for (int i = 0; i < DEFAULT_DATASET_SIZE; i++) { + cv.put("_ID", i); + cv.put("COL_A", i); + cv.put("COL_B", "NewValue"); + cv.put("COL_C", 1.0); + assertEquals(i, mDatabase.insert("T1", null, cv)); + } + cv = new ContentValues(); + for (int i = 0; i < DEFAULT_DATASET_SIZE; i++) { + cv.put("COL_B", "UpdatedValue"); + cv.put("COL_C", 1.1); + whereArg[0] = String.valueOf(i); + assertEquals(1, mDatabase.update("T1", cv, "_ID=?", whereArg)); + } + for (int i = 0; i < DEFAULT_DATASET_SIZE; i++) { + whereArg[0] = String.valueOf(i); + assertEquals(1, mDatabase.delete("T1", "_ID=?", whereArg)); + } + // Make sure all changes are written to disk + mDatabase.close(); + long bytes = endMeasuringWrites(); + sendResults("testDatabaseModifications" , bytes); + } + + @Test + public void testInsertsWithTransactions() { + startMeasuringWrites(); + final int txSize = 10; + ContentValues cv = new ContentValues(); + for (int i = 0; i < DEFAULT_DATASET_SIZE * 5; i++) { + if (i % txSize == 0) { + mDatabase.beginTransaction(); + } + if (i % txSize == txSize-1) { + mDatabase.setTransactionSuccessful(); + mDatabase.endTransaction(); + + } + cv.put("_ID", i); + cv.put("COL_A", i); + cv.put("COL_B", "NewValue"); + cv.put("COL_C", 1.0); + assertEquals(i, mDatabase.insert("T1", null, cv)); + } + // Make sure all changes are written to disk + mDatabase.close(); + long bytes = endMeasuringWrites(); + sendResults("testInsertsWithTransactions" , bytes); + } + + private void startMeasuringWrites() { + Preconditions.checkState(mWriteBytes == null, "Measurement already started"); + mWriteBytes = getIoStats().get("write_bytes"); + } + + private long endMeasuringWrites() { + Preconditions.checkState(mWriteBytes != null, "Measurement wasn't started"); + Long newWriteBytes = getIoStats().get("write_bytes"); + return newWriteBytes - mWriteBytes; + } + + private void sendResults(String testName, long writeBytes) { + Log.i(TAG, testName + " write_bytes: " + writeBytes); + Bundle status = new Bundle(); + status.putLong("write_bytes", writeBytes); + InstrumentationRegistry.getInstrumentation().sendStatus(Activity.RESULT_OK, status); + } + + private static Map<String, Long> getIoStats() { + String ioStat = "/proc/self/io"; + Map<String, Long> results = new ArrayMap<>(); + try { + List<String> lines = Files.readAllLines(new File(ioStat).toPath()); + for (String line : lines) { + line = line.trim(); + String[] split = line.split(":"); + if (split.length == 2) { + try { + String key = split[0].trim(); + Long value = Long.valueOf(split[1].trim()); + results.put(key, value); + } catch (NumberFormatException e) { + Log.e(TAG, "Cannot parse number from " + line); + } + } else if (line.isEmpty()) { + Log.e(TAG, "Cannot parse line " + line); + } + } + } catch (IOException e) { + Log.e(TAG, "Can't read: " + ioStat, e); + } + return results; + } + +} diff --git a/api/current.txt b/api/current.txt index 79816ac17b67..3e1440286444 100644 --- a/api/current.txt +++ b/api/current.txt @@ -35,6 +35,7 @@ package android { field public static final java.lang.String BIND_QUICK_SETTINGS_TILE = "android.permission.BIND_QUICK_SETTINGS_TILE"; field public static final java.lang.String BIND_REMOTEVIEWS = "android.permission.BIND_REMOTEVIEWS"; field public static final java.lang.String BIND_SCREENING_SERVICE = "android.permission.BIND_SCREENING_SERVICE"; + field public static final java.lang.String BIND_SLICE = "android.permission.BIND_SLICE"; field public static final java.lang.String BIND_TELECOM_CONNECTION_SERVICE = "android.permission.BIND_TELECOM_CONNECTION_SERVICE"; field public static final java.lang.String BIND_TEXT_SERVICE = "android.permission.BIND_TEXT_SERVICE"; field public static final java.lang.String BIND_TV_INPUT = "android.permission.BIND_TV_INPUT"; @@ -4006,8 +4007,10 @@ package android.app { } public static class ActivityManager.TaskDescription implements android.os.Parcelable { - ctor public ActivityManager.TaskDescription(java.lang.String, android.graphics.Bitmap, int); - ctor public ActivityManager.TaskDescription(java.lang.String, android.graphics.Bitmap); + ctor public deprecated ActivityManager.TaskDescription(java.lang.String, android.graphics.Bitmap, int); + ctor public ActivityManager.TaskDescription(java.lang.String, int, int); + ctor public deprecated ActivityManager.TaskDescription(java.lang.String, android.graphics.Bitmap); + ctor public ActivityManager.TaskDescription(java.lang.String, int); ctor public ActivityManager.TaskDescription(java.lang.String); ctor public ActivityManager.TaskDescription(); ctor public ActivityManager.TaskDescription(android.app.ActivityManager.TaskDescription); @@ -6941,6 +6944,88 @@ package android.app.job { } +package android.app.slice { + + public final class Slice implements android.os.Parcelable { + ctor protected Slice(android.os.Parcel); + method public static android.app.slice.Slice bindSlice(android.content.ContentResolver, android.net.Uri); + method public int describeContents(); + method public java.util.List<java.lang.String> getHints(); + method public java.util.List<android.app.slice.SliceItem> getItems(); + method public android.net.Uri getUri(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.app.slice.Slice> CREATOR; + field public static final java.lang.String HINT_ACTIONS = "actions"; + field public static final java.lang.String HINT_HORIZONTAL = "horizontal"; + field public static final java.lang.String HINT_LARGE = "large"; + field public static final java.lang.String HINT_LIST = "list"; + field public static final java.lang.String HINT_LIST_ITEM = "list_item"; + field public static final java.lang.String HINT_MESSAGE = "message"; + field public static final java.lang.String HINT_NO_TINT = "no_tint"; + field public static final java.lang.String HINT_PARTIAL = "partial"; + field public static final java.lang.String HINT_SELECTED = "selected"; + field public static final java.lang.String HINT_SOURCE = "source"; + field public static final java.lang.String HINT_TITLE = "title"; + } + + public static class Slice.Builder { + ctor public Slice.Builder(android.net.Uri); + ctor public Slice.Builder(android.app.slice.Slice.Builder); + method public android.app.slice.Slice.Builder addAction(android.app.PendingIntent, android.app.slice.Slice); + method public android.app.slice.Slice.Builder addColor(int, java.lang.String...); + method public android.app.slice.Slice.Builder addColor(int, java.util.List<java.lang.String>); + method public android.app.slice.Slice.Builder addHints(java.lang.String...); + method public android.app.slice.Slice.Builder addHints(java.util.List<java.lang.String>); + method public android.app.slice.Slice.Builder addIcon(android.graphics.drawable.Icon, java.lang.String...); + method public android.app.slice.Slice.Builder addIcon(android.graphics.drawable.Icon, java.util.List<java.lang.String>); + method public android.app.slice.Slice.Builder addRemoteInput(android.app.RemoteInput, java.util.List<java.lang.String>); + method public android.app.slice.Slice.Builder addRemoteInput(android.app.RemoteInput, java.lang.String...); + method public android.app.slice.Slice.Builder addSubSlice(android.app.slice.Slice); + method public android.app.slice.Slice.Builder addText(java.lang.CharSequence, java.lang.String...); + method public android.app.slice.Slice.Builder addText(java.lang.CharSequence, java.util.List<java.lang.String>); + method public android.app.slice.Slice.Builder addTimestamp(long, java.lang.String...); + method public android.app.slice.Slice.Builder addTimestamp(long, java.util.List<java.lang.String>); + method public android.app.slice.Slice build(); + } + + public final class SliceItem implements android.os.Parcelable { + method public int describeContents(); + method public android.app.PendingIntent getAction(); + method public int getColor(); + method public java.util.List<java.lang.String> getHints(); + method public android.graphics.drawable.Icon getIcon(); + method public android.app.RemoteInput getRemoteInput(); + method public android.app.slice.Slice getSlice(); + method public java.lang.CharSequence getText(); + method public long getTimestamp(); + method public int getType(); + method public boolean hasHint(java.lang.String); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.app.slice.SliceItem> CREATOR; + field public static final int TYPE_ACTION = 4; // 0x4 + field public static final int TYPE_COLOR = 6; // 0x6 + field public static final int TYPE_IMAGE = 3; // 0x3 + field public static final int TYPE_REMOTE_INPUT = 9; // 0x9 + field public static final int TYPE_SLICE = 1; // 0x1 + field public static final int TYPE_TEXT = 2; // 0x2 + field public static final int TYPE_TIMESTAMP = 8; // 0x8 + } + + public abstract class SliceProvider extends android.content.ContentProvider { + ctor public SliceProvider(); + method public final int delete(android.net.Uri, java.lang.String, java.lang.String[]); + method public final java.lang.String getType(android.net.Uri); + method public final android.net.Uri insert(android.net.Uri, android.content.ContentValues); + method public abstract android.app.slice.Slice onBindSlice(android.net.Uri); + method public final android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String); + method public final android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, android.os.CancellationSignal); + method public final android.database.Cursor query(android.net.Uri, java.lang.String[], android.os.Bundle, android.os.CancellationSignal); + method public final int update(android.net.Uri, android.content.ContentValues, java.lang.String, java.lang.String[]); + field public static final java.lang.String SLICE_TYPE = "vnd.android.slice"; + } + +} + package android.app.usage { public final class ConfigurationStats implements android.os.Parcelable { diff --git a/api/system-current.txt b/api/system-current.txt index 40683d359eaa..5138f6ed33be 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -58,6 +58,7 @@ package android { field public static final java.lang.String BIND_RUNTIME_PERMISSION_PRESENTER_SERVICE = "android.permission.BIND_RUNTIME_PERMISSION_PRESENTER_SERVICE"; field public static final java.lang.String BIND_SCREENING_SERVICE = "android.permission.BIND_SCREENING_SERVICE"; field public static final java.lang.String BIND_SETTINGS_SUGGESTIONS_SERVICE = "android.permission.BIND_SETTINGS_SUGGESTIONS_SERVICE"; + field public static final java.lang.String BIND_SLICE = "android.permission.BIND_SLICE"; field public static final java.lang.String BIND_TELECOM_CONNECTION_SERVICE = "android.permission.BIND_TELECOM_CONNECTION_SERVICE"; field public static final java.lang.String BIND_TEXT_SERVICE = "android.permission.BIND_TEXT_SERVICE"; field public static final java.lang.String BIND_TRUST_AGENT = "android.permission.BIND_TRUST_AGENT"; @@ -4168,8 +4169,10 @@ package android.app { } public static class ActivityManager.TaskDescription implements android.os.Parcelable { - ctor public ActivityManager.TaskDescription(java.lang.String, android.graphics.Bitmap, int); - ctor public ActivityManager.TaskDescription(java.lang.String, android.graphics.Bitmap); + ctor public deprecated ActivityManager.TaskDescription(java.lang.String, android.graphics.Bitmap, int); + ctor public ActivityManager.TaskDescription(java.lang.String, int, int); + ctor public deprecated ActivityManager.TaskDescription(java.lang.String, android.graphics.Bitmap); + ctor public ActivityManager.TaskDescription(java.lang.String, int); ctor public ActivityManager.TaskDescription(java.lang.String); ctor public ActivityManager.TaskDescription(); ctor public ActivityManager.TaskDescription(android.app.ActivityManager.TaskDescription); @@ -7384,6 +7387,88 @@ package android.app.job { } +package android.app.slice { + + public final class Slice implements android.os.Parcelable { + ctor protected Slice(android.os.Parcel); + method public static android.app.slice.Slice bindSlice(android.content.ContentResolver, android.net.Uri); + method public int describeContents(); + method public java.util.List<java.lang.String> getHints(); + method public java.util.List<android.app.slice.SliceItem> getItems(); + method public android.net.Uri getUri(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.app.slice.Slice> CREATOR; + field public static final java.lang.String HINT_ACTIONS = "actions"; + field public static final java.lang.String HINT_HORIZONTAL = "horizontal"; + field public static final java.lang.String HINT_LARGE = "large"; + field public static final java.lang.String HINT_LIST = "list"; + field public static final java.lang.String HINT_LIST_ITEM = "list_item"; + field public static final java.lang.String HINT_MESSAGE = "message"; + field public static final java.lang.String HINT_NO_TINT = "no_tint"; + field public static final java.lang.String HINT_PARTIAL = "partial"; + field public static final java.lang.String HINT_SELECTED = "selected"; + field public static final java.lang.String HINT_SOURCE = "source"; + field public static final java.lang.String HINT_TITLE = "title"; + } + + public static class Slice.Builder { + ctor public Slice.Builder(android.net.Uri); + ctor public Slice.Builder(android.app.slice.Slice.Builder); + method public android.app.slice.Slice.Builder addAction(android.app.PendingIntent, android.app.slice.Slice); + method public android.app.slice.Slice.Builder addColor(int, java.lang.String...); + method public android.app.slice.Slice.Builder addColor(int, java.util.List<java.lang.String>); + method public android.app.slice.Slice.Builder addHints(java.lang.String...); + method public android.app.slice.Slice.Builder addHints(java.util.List<java.lang.String>); + method public android.app.slice.Slice.Builder addIcon(android.graphics.drawable.Icon, java.lang.String...); + method public android.app.slice.Slice.Builder addIcon(android.graphics.drawable.Icon, java.util.List<java.lang.String>); + method public android.app.slice.Slice.Builder addRemoteInput(android.app.RemoteInput, java.util.List<java.lang.String>); + method public android.app.slice.Slice.Builder addRemoteInput(android.app.RemoteInput, java.lang.String...); + method public android.app.slice.Slice.Builder addSubSlice(android.app.slice.Slice); + method public android.app.slice.Slice.Builder addText(java.lang.CharSequence, java.lang.String...); + method public android.app.slice.Slice.Builder addText(java.lang.CharSequence, java.util.List<java.lang.String>); + method public android.app.slice.Slice.Builder addTimestamp(long, java.lang.String...); + method public android.app.slice.Slice.Builder addTimestamp(long, java.util.List<java.lang.String>); + method public android.app.slice.Slice build(); + } + + public final class SliceItem implements android.os.Parcelable { + method public int describeContents(); + method public android.app.PendingIntent getAction(); + method public int getColor(); + method public java.util.List<java.lang.String> getHints(); + method public android.graphics.drawable.Icon getIcon(); + method public android.app.RemoteInput getRemoteInput(); + method public android.app.slice.Slice getSlice(); + method public java.lang.CharSequence getText(); + method public long getTimestamp(); + method public int getType(); + method public boolean hasHint(java.lang.String); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.app.slice.SliceItem> CREATOR; + field public static final int TYPE_ACTION = 4; // 0x4 + field public static final int TYPE_COLOR = 6; // 0x6 + field public static final int TYPE_IMAGE = 3; // 0x3 + field public static final int TYPE_REMOTE_INPUT = 9; // 0x9 + field public static final int TYPE_SLICE = 1; // 0x1 + field public static final int TYPE_TEXT = 2; // 0x2 + field public static final int TYPE_TIMESTAMP = 8; // 0x8 + } + + public abstract class SliceProvider extends android.content.ContentProvider { + ctor public SliceProvider(); + method public final int delete(android.net.Uri, java.lang.String, java.lang.String[]); + method public final java.lang.String getType(android.net.Uri); + method public final android.net.Uri insert(android.net.Uri, android.content.ContentValues); + method public abstract android.app.slice.Slice onBindSlice(android.net.Uri); + method public final android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String); + method public final android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, android.os.CancellationSignal); + method public final android.database.Cursor query(android.net.Uri, java.lang.String[], android.os.Bundle, android.os.CancellationSignal); + method public final int update(android.net.Uri, android.content.ContentValues, java.lang.String, java.lang.String[]); + field public static final java.lang.String SLICE_TYPE = "vnd.android.slice"; + } + +} + package android.app.usage { public final class CacheQuotaHint implements android.os.Parcelable { diff --git a/api/test-current.txt b/api/test-current.txt index cb4a6f2f9414..bd09f68870c1 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -35,6 +35,7 @@ package android { field public static final java.lang.String BIND_QUICK_SETTINGS_TILE = "android.permission.BIND_QUICK_SETTINGS_TILE"; field public static final java.lang.String BIND_REMOTEVIEWS = "android.permission.BIND_REMOTEVIEWS"; field public static final java.lang.String BIND_SCREENING_SERVICE = "android.permission.BIND_SCREENING_SERVICE"; + field public static final java.lang.String BIND_SLICE = "android.permission.BIND_SLICE"; field public static final java.lang.String BIND_TELECOM_CONNECTION_SERVICE = "android.permission.BIND_TELECOM_CONNECTION_SERVICE"; field public static final java.lang.String BIND_TEXT_SERVICE = "android.permission.BIND_TEXT_SERVICE"; field public static final java.lang.String BIND_TV_INPUT = "android.permission.BIND_TV_INPUT"; @@ -4026,13 +4027,17 @@ package android.app { } public static class ActivityManager.TaskDescription implements android.os.Parcelable { - ctor public ActivityManager.TaskDescription(java.lang.String, android.graphics.Bitmap, int); - ctor public ActivityManager.TaskDescription(java.lang.String, android.graphics.Bitmap); + ctor public deprecated ActivityManager.TaskDescription(java.lang.String, android.graphics.Bitmap, int); + ctor public ActivityManager.TaskDescription(java.lang.String, int, int); + ctor public deprecated ActivityManager.TaskDescription(java.lang.String, android.graphics.Bitmap); + ctor public ActivityManager.TaskDescription(java.lang.String, int); ctor public ActivityManager.TaskDescription(java.lang.String); ctor public ActivityManager.TaskDescription(); ctor public ActivityManager.TaskDescription(android.app.ActivityManager.TaskDescription); method public int describeContents(); method public android.graphics.Bitmap getIcon(); + method public java.lang.String getIconFilename(); + method public int getIconResource(); method public java.lang.String getLabel(); method public int getPrimaryColor(); method public void readFromParcel(android.os.Parcel); @@ -7010,6 +7015,88 @@ package android.app.job { } +package android.app.slice { + + public final class Slice implements android.os.Parcelable { + ctor protected Slice(android.os.Parcel); + method public static android.app.slice.Slice bindSlice(android.content.ContentResolver, android.net.Uri); + method public int describeContents(); + method public java.util.List<java.lang.String> getHints(); + method public java.util.List<android.app.slice.SliceItem> getItems(); + method public android.net.Uri getUri(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.app.slice.Slice> CREATOR; + field public static final java.lang.String HINT_ACTIONS = "actions"; + field public static final java.lang.String HINT_HORIZONTAL = "horizontal"; + field public static final java.lang.String HINT_LARGE = "large"; + field public static final java.lang.String HINT_LIST = "list"; + field public static final java.lang.String HINT_LIST_ITEM = "list_item"; + field public static final java.lang.String HINT_MESSAGE = "message"; + field public static final java.lang.String HINT_NO_TINT = "no_tint"; + field public static final java.lang.String HINT_PARTIAL = "partial"; + field public static final java.lang.String HINT_SELECTED = "selected"; + field public static final java.lang.String HINT_SOURCE = "source"; + field public static final java.lang.String HINT_TITLE = "title"; + } + + public static class Slice.Builder { + ctor public Slice.Builder(android.net.Uri); + ctor public Slice.Builder(android.app.slice.Slice.Builder); + method public android.app.slice.Slice.Builder addAction(android.app.PendingIntent, android.app.slice.Slice); + method public android.app.slice.Slice.Builder addColor(int, java.lang.String...); + method public android.app.slice.Slice.Builder addColor(int, java.util.List<java.lang.String>); + method public android.app.slice.Slice.Builder addHints(java.lang.String...); + method public android.app.slice.Slice.Builder addHints(java.util.List<java.lang.String>); + method public android.app.slice.Slice.Builder addIcon(android.graphics.drawable.Icon, java.lang.String...); + method public android.app.slice.Slice.Builder addIcon(android.graphics.drawable.Icon, java.util.List<java.lang.String>); + method public android.app.slice.Slice.Builder addRemoteInput(android.app.RemoteInput, java.util.List<java.lang.String>); + method public android.app.slice.Slice.Builder addRemoteInput(android.app.RemoteInput, java.lang.String...); + method public android.app.slice.Slice.Builder addSubSlice(android.app.slice.Slice); + method public android.app.slice.Slice.Builder addText(java.lang.CharSequence, java.lang.String...); + method public android.app.slice.Slice.Builder addText(java.lang.CharSequence, java.util.List<java.lang.String>); + method public android.app.slice.Slice.Builder addTimestamp(long, java.lang.String...); + method public android.app.slice.Slice.Builder addTimestamp(long, java.util.List<java.lang.String>); + method public android.app.slice.Slice build(); + } + + public final class SliceItem implements android.os.Parcelable { + method public int describeContents(); + method public android.app.PendingIntent getAction(); + method public int getColor(); + method public java.util.List<java.lang.String> getHints(); + method public android.graphics.drawable.Icon getIcon(); + method public android.app.RemoteInput getRemoteInput(); + method public android.app.slice.Slice getSlice(); + method public java.lang.CharSequence getText(); + method public long getTimestamp(); + method public int getType(); + method public boolean hasHint(java.lang.String); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.app.slice.SliceItem> CREATOR; + field public static final int TYPE_ACTION = 4; // 0x4 + field public static final int TYPE_COLOR = 6; // 0x6 + field public static final int TYPE_IMAGE = 3; // 0x3 + field public static final int TYPE_REMOTE_INPUT = 9; // 0x9 + field public static final int TYPE_SLICE = 1; // 0x1 + field public static final int TYPE_TEXT = 2; // 0x2 + field public static final int TYPE_TIMESTAMP = 8; // 0x8 + } + + public abstract class SliceProvider extends android.content.ContentProvider { + ctor public SliceProvider(); + method public final int delete(android.net.Uri, java.lang.String, java.lang.String[]); + method public final java.lang.String getType(android.net.Uri); + method public final android.net.Uri insert(android.net.Uri, android.content.ContentValues); + method public abstract android.app.slice.Slice onBindSlice(android.net.Uri); + method public final android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String); + method public final android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, android.os.CancellationSignal); + method public final android.database.Cursor query(android.net.Uri, java.lang.String[], android.os.Bundle, android.os.CancellationSignal); + method public final int update(android.net.Uri, android.content.ContentValues, java.lang.String, java.lang.String[]); + field public static final java.lang.String SLICE_TYPE = "vnd.android.slice"; + } + +} + package android.app.usage { public final class ConfigurationStats implements android.os.Parcelable { @@ -35400,6 +35487,7 @@ package android.provider { method public static boolean putString(android.content.ContentResolver, java.lang.String, java.lang.String); method public static final deprecated void setLocationProviderEnabled(android.content.ContentResolver, java.lang.String, boolean); field public static final java.lang.String ACCESSIBILITY_DISPLAY_INVERSION_ENABLED = "accessibility_display_inversion_enabled"; + field public static final java.lang.String ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED = "accessibility_display_magnification_enabled"; field public static final java.lang.String ACCESSIBILITY_ENABLED = "accessibility_enabled"; field public static final deprecated java.lang.String ACCESSIBILITY_SPEAK_PASSWORD = "speak_password"; field public static final deprecated java.lang.String ADB_ENABLED = "adb_enabled"; diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java index c5c38f530912..60ec8a96f325 100644 --- a/cmds/pm/src/com/android/commands/pm/Pm.java +++ b/cmds/pm/src/com/android/commands/pm/Pm.java @@ -1570,11 +1570,19 @@ public final class Pm { private static int showUsage() { System.err.println("usage: pm path [--user USER_ID] PACKAGE"); System.err.println(" pm dump PACKAGE"); - System.err.println(" pm install [-lrtsfd] [-i PACKAGE] [--user USER_ID] [PATH]"); - System.err.println(" pm install-create [-lrtsfdp] [-i PACKAGE] [-S BYTES]"); - System.err.println(" [--install-location 0/1/2]"); - System.err.println(" [--force-uuid internal|UUID]"); - System.err.println(" pm install-write [-S BYTES] SESSION_ID SPLIT_NAME [PATH]"); + System.err.println(" pm install [-lrtsfdg] [-i PACKAGE] [--user USER_ID]"); + System.err.println(" [-p INHERIT_PACKAGE] [--install-location 0/1/2]"); + System.err.println(" [--originating-uri URI] [---referrer URI]"); + System.err.println(" [--abi ABI_NAME] [--force-sdk]"); + System.err.println(" [--preload] [--instantapp] [--full] [--dont-kill]"); + System.err.println(" [--force-uuid internal|UUID] [--pkg PACKAGE] [-S BYTES] [PATH|-]"); + System.err.println(" pm install-create [-lrtsfdg] [-i PACKAGE] [--user USER_ID]"); + System.err.println(" [-p INHERIT_PACKAGE] [--install-location 0/1/2]"); + System.err.println(" [--originating-uri URI] [---referrer URI]"); + System.err.println(" [--abi ABI_NAME] [--force-sdk]"); + System.err.println(" [--preload] [--instantapp] [--full] [--dont-kill]"); + System.err.println(" [--force-uuid internal|UUID] [--pkg PACKAGE] [-S BYTES]"); + System.err.println(" pm install-write [-S BYTES] SESSION_ID SPLIT_NAME [PATH|-]"); System.err.println(" pm install-commit SESSION_ID"); System.err.println(" pm install-abandon SESSION_ID"); System.err.println(" pm uninstall [-k] [--user USER_ID] [--versionCode VERSION_CODE] PACKAGE"); @@ -1613,15 +1621,27 @@ public final class Pm { System.err.println("pm install: install a single legacy package"); System.err.println("pm install-create: create an install session"); System.err.println(" -l: forward lock application"); - System.err.println(" -r: replace existing application"); + System.err.println(" -r: allow replacement of existing application"); System.err.println(" -t: allow test packages"); - System.err.println(" -i: specify the installer package name"); + System.err.println(" -i: specify package name of installer owning the app"); System.err.println(" -s: install application on sdcard"); System.err.println(" -f: install application on internal flash"); System.err.println(" -d: allow version code downgrade (debuggable packages only)"); - System.err.println(" -p: partial application install"); + System.err.println(" -p: partial application install (new split on top of existing pkg)"); System.err.println(" -g: grant all runtime permissions"); System.err.println(" -S: size in bytes of entire session"); + System.err.println(" --dont-kill: installing a new feature split, don't kill running app"); + System.err.println(" --originating-uri: set URI where app was downloaded from"); + System.err.println(" --referrer: set URI that instigated the install of the app"); + System.err.println(" --pkg: specify expected package name of app being installed"); + System.err.println(" --abi: override the default ABI of the platform"); + System.err.println(" --instantapp: cause the app to be installed as an ephemeral install app"); + System.err.println(" --full: cause the app to be installed as a non-ephemeral full app"); + System.err.println(" --install-location: force the install location:"); + System.err.println(" 0=auto, 1=internal only, 2=prefer external"); + System.err.println(" --force-uuid: force install on to disk volume with given UUID"); + System.err.println(" --force-sdk: allow install even when existing app targets platform"); + System.err.println(" codename but new one targets a final API level"); System.err.println(""); System.err.println("pm install-write: write a package into existing session; path may"); System.err.println(" be '-' to read from stdin"); diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp index 5586057a2e70..4ebca8430cf4 100644 --- a/cmds/statsd/Android.bp +++ b/cmds/statsd/Android.bp @@ -21,8 +21,6 @@ cc_library_host_shared { name: "libstats_proto_host", srcs: [ "src/stats_events.proto", - "src/stats_log.proto", - "src/statsd_config.proto", ], shared_libs: [ diff --git a/cmds/statsd/Android.mk b/cmds/statsd/Android.mk index d9c37ef0c9b8..a8d3e1268b38 100644 --- a/cmds/statsd/Android.mk +++ b/cmds/statsd/Android.mk @@ -14,16 +14,23 @@ LOCAL_PATH:= $(call my-dir) - statsd_common_src := \ ../../core/java/android/os/IStatsCompanionService.aidl \ ../../core/java/android/os/IStatsManager.aidl \ src/stats_log.proto \ src/statsd_config.proto \ - src/stats_events.proto \ + src/stats_events_copy.proto \ + src/anomaly/AnomalyMonitor.cpp \ src/condition/CombinationConditionTracker.cpp \ src/condition/condition_util.cpp \ src/condition/SimpleConditionTracker.cpp \ + src/config/ConfigKey.cpp \ + src/config/ConfigListener.cpp \ + src/config/ConfigManager.cpp \ + src/external/KernelWakelockPuller.cpp \ + src/external/StatsPullerManager.cpp \ + src/logd/LogListener.cpp \ + src/logd/LogReader.cpp \ src/matchers/CombinationLogMatchingTracker.cpp \ src/matchers/matcher_util.cpp \ src/matchers/SimpleLogMatchingTracker.cpp \ @@ -31,17 +38,12 @@ statsd_common_src := \ src/metrics/CountMetricProducer.cpp \ src/metrics/MetricsManager.cpp \ src/metrics/metrics_manager_util.cpp \ - src/AnomalyMonitor.cpp \ - src/DropboxReader.cpp \ - src/DropboxWriter.cpp \ - src/KernelWakelockPuller.cpp \ - src/LogEntryPrinter.cpp \ - src/LogReader.cpp \ + src/packages/UidMap.cpp \ + src/storage/DropboxReader.cpp \ + src/storage/DropboxWriter.cpp \ src/StatsLogProcessor.cpp \ - src/StatsPullerManager.cpp \ src/StatsService.cpp \ - src/stats_util.cpp \ - src/UidMap.cpp + src/stats_util.cpp statsd_common_c_includes := \ $(LOCAL_PATH)/src @@ -125,13 +127,15 @@ LOCAL_CFLAGS += \ LOCAL_SRC_FILES := \ $(statsd_common_src) \ + tests/AnomalyMonitor_test.cpp \ + tests/ConditionTracker_test.cpp \ + tests/ConfigManager_test.cpp \ tests/indexed_priority_queue_test.cpp \ + tests/LogEntryMatcher_test.cpp \ tests/LogReader_test.cpp \ tests/MetricsManager_test.cpp \ - tests/UidMap_test.cpp \ - tests/LogEntryMatcher_test.cpp \ - tests/AnomalyMonitor_test.cpp \ - tests/ConditionTracker_test.cpp + tests/UidMap_test.cpp + LOCAL_STATIC_LIBRARIES := \ libgmock diff --git a/cmds/statsd/src/Log.h b/cmds/statsd/src/Log.h new file mode 100644 index 000000000000..785270973fd0 --- /dev/null +++ b/cmds/statsd/src/Log.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * This file must be included at the top of the file. Other header files + * occasionally include log.h, and if LOG_TAG isn't set when that happens + * we'll get a preprocesser error when we try to define it here. + */ + +#pragma once + +#define LOG_TAG "statsd" + +#include <log/log.h> + +#define VLOG(...) \ + if (DEBUG) ALOGD(__VA_ARGS__); diff --git a/cmds/statsd/src/LogEntryPrinter.cpp b/cmds/statsd/src/LogEntryPrinter.cpp deleted file mode 100644 index 3b6f6791d7f4..000000000000 --- a/cmds/statsd/src/LogEntryPrinter.cpp +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <LogEntryPrinter.h> - -#include <log/event_tag_map.h> -#include <log/logprint.h> -#include <utils/Errors.h> - -#include "matchers/matcher_util.h" - -#define PRINT_WITH_LIBLOG 0 -#define PRINT_WITH_LOG_EVENT_WRAPPER 1 - -using namespace android; - -namespace android { -namespace os { -namespace statsd { - -LogEntryPrinter::LogEntryPrinter(int out) : m_out(out) { - // Initialize the EventTagMap, which is how we know the names of the numeric event tags. - // If this fails, we can't print well, but something will print. - m_tags = android_openEventTagMap(NULL); - - // Printing format - m_format = android_log_format_new(); - android_log_setPrintFormat(m_format, FORMAT_THREADTIME); -} - -LogEntryPrinter::~LogEntryPrinter() { - if (m_tags != NULL) { - android_closeEventTagMap(m_tags); - } - android_log_format_free(m_format); -} - -void LogEntryPrinter::OnLogEvent(const log_msg& msg) { - if (PRINT_WITH_LIBLOG) { - status_t err; - AndroidLogEntry entry; - char buf[1024]; - - err = android_log_processBinaryLogBuffer(&(const_cast<log_msg*>(&msg)->entry_v1), &entry, - m_tags, buf, sizeof(buf)); - if (err == NO_ERROR) { - android_log_printLogLine(m_format, m_out, &entry); - } else { - printf("log entry: %s\n", buf); - fflush(stdout); - } - } - - if (PRINT_WITH_LOG_EVENT_WRAPPER) { - LogEventWrapper event = parseLogEvent(msg); - printf("event: %s\n", event.toString().c_str()); - fflush(stdout); - } -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp index f877ef30432c..1308ca1fb4eb 100644 --- a/cmds/statsd/src/StatsLogProcessor.cpp +++ b/cmds/statsd/src/StatsLogProcessor.cpp @@ -14,12 +14,14 @@ * limitations under the License. */ -#include <StatsLogProcessor.h> +#include "Log.h" + +#include "StatsLogProcessor.h" +#include "frameworks/base/cmds/statsd/src/stats_log.pb.h" +#include "metrics/CountMetricProducer.h" +#include "stats_util.h" -#include <cutils/log.h> -#include <frameworks/base/cmds/statsd/src/stats_log.pb.h> #include <log/log_event_list.h> -#include <metrics/CountMetricProducer.h> #include <utils/Errors.h> using namespace android; @@ -31,12 +33,8 @@ namespace android { namespace os { namespace statsd { -StatsLogProcessor::StatsLogProcessor(const sp<UidMap> &uidMap) - : m_dropbox_writer("all-logs"), m_UidMap(uidMap) -{ - // hardcoded config - // this should be called from StatsService when it receives a statsd_config - UpdateConfig(0, buildFakeConfig()); +StatsLogProcessor::StatsLogProcessor(const sp<UidMap>& uidMap) + : m_dropbox_writer("all-logs"), mUidMap(uidMap) { } StatsLogProcessor::~StatsLogProcessor() { @@ -54,17 +52,18 @@ void StatsLogProcessor::OnLogEvent(const log_msg& msg) { } } -void StatsLogProcessor::UpdateConfig(const int config_source, const StatsdConfig& config) { - auto it = mMetricsManagers.find(config_source); +void StatsLogProcessor::OnConfigUpdated(const ConfigKey& key, const StatsdConfig& config) { + auto it = mMetricsManagers.find(key); if (it != mMetricsManagers.end()) { it->second->finish(); } - ALOGD("Updated configuration for source %i", config_source); + ALOGD("Updated configuration for key %s", key.ToString().c_str()); unique_ptr<MetricsManager> newMetricsManager = std::make_unique<MetricsManager>(config); if (newMetricsManager->isConfigValid()) { - mMetricsManagers.insert({config_source, std::move(newMetricsManager)}); + mMetricsManagers[key] = std::move(newMetricsManager); + // Why doesn't this work? mMetricsManagers.insert({key, std::move(newMetricsManager)}); ALOGD("StatsdConfig valid"); } else { // If there is any error in the config, don't use it. @@ -72,6 +71,14 @@ void StatsLogProcessor::UpdateConfig(const int config_source, const StatsdConfig } } +void StatsLogProcessor::OnConfigRemoved(const ConfigKey& key) { + auto it = mMetricsManagers.find(key); + if (it != mMetricsManagers.end()) { + it->second->finish(); + mMetricsManagers.erase(it); + } +} + } // namespace statsd } // namespace os } // namespace android diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h index 05e441caa496..6f5dd04876e1 100644 --- a/cmds/statsd/src/StatsLogProcessor.h +++ b/cmds/statsd/src/StatsLogProcessor.h @@ -16,12 +16,13 @@ #ifndef STATS_LOG_PROCESSOR_H #define STATS_LOG_PROCESSOR_H -#include "DropboxWriter.h" -#include "LogReader.h" -#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" +#include "config/ConfigListener.h" +#include "logd/LogReader.h" #include "metrics/MetricsManager.h" -#include "stats_util.h" -#include "UidMap.h" +#include "packages/UidMap.h" +#include "storage/DropboxWriter.h" + +#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" #include <log/logprint.h> #include <stdio.h> @@ -31,22 +32,23 @@ namespace android { namespace os { namespace statsd { -class StatsLogProcessor : public LogListener { +class StatsLogProcessor : public ConfigListener { public: StatsLogProcessor(const sp<UidMap> &uidMap); virtual ~StatsLogProcessor(); virtual void OnLogEvent(const log_msg& msg); - void UpdateConfig(const int config_source, const StatsdConfig& config); + void OnConfigUpdated(const ConfigKey& key, const StatsdConfig& config); + void OnConfigRemoved(const ConfigKey& key); private: // TODO: use EventMetrics to log the events. DropboxWriter m_dropbox_writer; - std::unordered_map<int, std::unique_ptr<MetricsManager>> mMetricsManagers; + std::unordered_map<ConfigKey, std::unique_ptr<MetricsManager>> mMetricsManagers; - sp<UidMap> m_UidMap; // Reference to the UidMap to lookup app name and version for each uid. + sp<UidMap> mUidMap; // Reference to the UidMap to lookup app name and version for each uid. }; } // namespace statsd diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp index b496404962d5..eff2c1cfbcec 100644 --- a/cmds/statsd/src/StatsService.cpp +++ b/cmds/statsd/src/StatsService.cpp @@ -14,16 +14,15 @@ * limitations under the License. */ -#define LOG_TAG "statsd" #define DEBUG true +#include "Log.h" #include "StatsService.h" -#include "DropboxReader.h" +#include "storage/DropboxReader.h" #include <android-base/file.h> #include <binder/IPCThreadState.h> #include <binder/IServiceManager.h> -#include <cutils/log.h> #include <frameworks/base/cmds/statsd/src/statsd_config.pb.h> #include <private/android_filesystem_config.h> #include <utils/Looper.h> @@ -31,6 +30,7 @@ #include <stdio.h> #include <stdlib.h> +#include <sys/system_properties.h> #include <unistd.h> using namespace android; @@ -39,24 +39,65 @@ namespace android { namespace os { namespace statsd { +// ====================================================================== +/** + * Watches for the death of the stats companion (system process). + */ +class CompanionDeathRecipient : public IBinder::DeathRecipient { +public: + CompanionDeathRecipient(const sp<AnomalyMonitor>& anomalyMonitor); + virtual void binderDied(const wp<IBinder>& who); + +private: + const sp<AnomalyMonitor> mAnomalyMonitor; +}; + +CompanionDeathRecipient::CompanionDeathRecipient(const sp<AnomalyMonitor>& anomalyMonitor) + : mAnomalyMonitor(anomalyMonitor) { +} + +void CompanionDeathRecipient::binderDied(const wp<IBinder>& who) { + ALOGW("statscompanion service died"); + mAnomalyMonitor->setStatsCompanionService(nullptr); +} + +// ====================================================================== StatsService::StatsService(const sp<Looper>& handlerLooper) - : mAnomalyMonitor(new AnomalyMonitor(2)),m_UidMap(new UidMap()), mStatsPullerManager() - // TODO: Change AnomalyMonitor initialization based on the config + : mStatsPullerManager(), + + mAnomalyMonitor(new AnomalyMonitor(2)) // TODO: Put this comment somewhere better { - ALOGD("stats service constructed"); + mUidMap = new UidMap(); + mConfigManager = new ConfigManager(); + mProcessor = new StatsLogProcessor(mUidMap); + + mConfigManager->AddListener(mProcessor); + + init_system_properties(); } StatsService::~StatsService() { } -status_t StatsService::setProcessor(const sp<StatsLogProcessor>& main_processor) { - m_processor = main_processor; - ALOGD("stats service set to processor %p", m_processor.get()); - return NO_ERROR; +void StatsService::init_system_properties() { + mEngBuild = false; + const prop_info* buildType = __system_property_find("ro.build.type"); + if (buildType != NULL) { + __system_property_read_callback(buildType, init_build_type_callback, this); + } } -// Implement our own because the default binder implementation isn't -// properly handling SHELL_COMMAND_TRANSACTION +void StatsService::init_build_type_callback(void* cookie, const char* /*name*/, const char* value, + uint32_t serial) { + if (0 == strcmp("eng", value)) { + reinterpret_cast<StatsService*>(cookie)->mEngBuild = true; + } +} + +/** + * Implement our own because the default binder implementation isn't + * properly handling SHELL_COMMAND_TRANSACTION. + */ status_t StatsService::onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { status_t err; @@ -105,56 +146,159 @@ status_t StatsService::onTransact(uint32_t code, const Parcel& data, Parcel* rep } } +/** + * Write debugging data about statsd. + */ status_t StatsService::dump(int fd, const Vector<String16>& args) { FILE* out = fdopen(fd, "w"); if (out == NULL) { return NO_MEMORY; // the fd is already open } - fprintf(out, "StatsService::dump:"); - ALOGD("StatsService::dump:"); - const int N = args.size(); - for (int i = 0; i < N; i++) { - fprintf(out, " %s", String8(args[i]).string()); - ALOGD(" %s", String8(args[i]).string()); - } - fprintf(out, "\n"); + // TODO: Proto format for incident reports + dump_impl(out); fclose(out); return NO_ERROR; } +/** + * Write debugging data about statsd in text format. + */ +void StatsService::dump_impl(FILE* out) { + mConfigManager->Dump(out); +} + +/** + * Implementation of the adb shell cmd stats command. + */ status_t StatsService::command(FILE* in, FILE* out, FILE* err, Vector<String8>& args) { - if (args.size() > 0) { - if (!args[0].compare(String8("print-stats-log")) && args.size() > 1) { - return doPrintStatsLog(out, args); - } + // TODO: Permission check + + const int argCount = args.size(); + if (argCount >= 1) { + // adb shell cmd stats config ... if (!args[0].compare(String8("config"))) { - return doLoadConfig(in); + return cmd_config(in, out, err, args); + } + + // adb shell cmd stats print-stats-log + if (!args[0].compare(String8("print-stats-log")) && args.size() > 1) { + return cmd_print_stats_log(out, args); } + + // adb shell cmd stats print-stats-log if (!args[0].compare(String8("print-uid-map"))) { - return doPrintUidMap(out); + return cmd_print_uid_map(out); } } - printCmdHelp(out); + print_cmd_help(out); return NO_ERROR; } -status_t StatsService::doLoadConfig(FILE* in) { - string content; - if (!android::base::ReadFdToString(fileno(in), &content)) { - return UNKNOWN_ERROR; +void StatsService::print_cmd_help(FILE* out) { + fprintf(out, + "usage: adb shell cmd stats print-stats-log [tag_required] " + "[timestamp_nsec_optional]\n"); + fprintf(out, "\n"); + fprintf(out, "\n"); + fprintf(out, "usage: adb shell cmd stats print-uid-map \n"); + fprintf(out, "\n"); + fprintf(out, " Prints the UID, app name, version mapping.\n"); + fprintf(out, "\n"); + fprintf(out, "\n"); + fprintf(out, "usage: adb shell cmd stats config remove [UID] NAME\n"); + fprintf(out, "usage: adb shell cmd stats config update [UID] NAME\n"); + fprintf(out, "\n"); + fprintf(out, " Adds, updates or removes a configuration. The proto should be in\n"); + fprintf(out, " wire-encoded protobuf format and passed via stdin.\n"); + fprintf(out, "\n"); + fprintf(out, " UID The uid to use. It is only possible to pass the UID\n"); + fprintf(out, " parameter on eng builds. If UID is omitted the calling\n"); + fprintf(out, " uid is used.\n"); + fprintf(out, " NAME The per-uid name to use\n"); +} + +status_t StatsService::cmd_config(FILE* in, FILE* out, FILE* err, Vector<String8>& args) { + const int argCount = args.size(); + if (argCount >= 2) { + if (args[1] == "update" || args[1] == "remove") { + bool good = false; + int uid = -1; + string name; + + if (argCount == 3) { + // Automatically pick the UID + uid = IPCThreadState::self()->getCallingUid(); + // TODO: What if this isn't a binder call? Should we fail? + name.assign(args[2].c_str(), args[2].size()); + good = true; + } else if (argCount == 4) { + // If it's a userdebug or eng build, then the shell user can + // impersonate other uids. + if (mEngBuild) { + const char* s = args[2].c_str(); + if (*s != '\0') { + char* end = NULL; + uid = strtol(s, &end, 0); + if (*end == '\0') { + name.assign(args[3].c_str(), args[3].size()); + good = true; + } + } + } else { + fprintf(err, "The config can only be set for other UIDs on eng builds.\n"); + } + } + + if (!good) { + // If arg parsing failed, print the help text and return an error. + print_cmd_help(out); + return UNKNOWN_ERROR; + } + + if (args[1] == "update") { + // Read stream into buffer. + string buffer; + if (!android::base::ReadFdToString(fileno(in), &buffer)) { + fprintf(err, "Error reading stream for StatsConfig.\n"); + return UNKNOWN_ERROR; + } + + // Parse buffer. + StatsdConfig config; + if (!config.ParseFromString(buffer)) { + fprintf(err, "Error parsing proto stream for StatsConfig.\n"); + return UNKNOWN_ERROR; + } + + // Add / update the config. + mConfigManager->UpdateConfig(ConfigKey(uid, name), config); + } else { + // Remove the config. + mConfigManager->RemoveConfig(ConfigKey(uid, name)); + } + + return NO_ERROR; + } } - StatsdConfig config; - if (config.ParseFromString(content)) { - ALOGD("Config parsed from command line: %s", config.SerializeAsString().c_str()); - m_processor->UpdateConfig(0, config); - return NO_ERROR; - } else { - ALOGD("Config failed to be parsed"); - return UNKNOWN_ERROR; + print_cmd_help(out); + return UNKNOWN_ERROR; +} + +status_t StatsService::cmd_print_stats_log(FILE* out, const Vector<String8>& args) { + long msec = 0; + + if (args.size() > 2) { + msec = strtol(args[2].string(), NULL, 10); } + return DropboxReader::readStatsLogs(out, args[1].string(), msec); +} + +status_t StatsService::cmd_print_uid_map(FILE* out) { + mUidMap->printUidMap(out); + return NO_ERROR; } Status StatsService::informAllUidData(const vector<int32_t>& uid, const vector<int32_t>& version, @@ -166,7 +310,7 @@ Status StatsService::informAllUidData(const vector<int32_t>& uid, const vector<i "Only system uid can call informAllUidData"); } - m_UidMap->updateMap(uid, version, app); + mUidMap->updateMap(uid, version, app); if (DEBUG) ALOGD("StatsService::informAllUidData succeeded"); return Status::ok(); @@ -179,7 +323,7 @@ Status StatsService::informOnePackage(const String16& app, int32_t uid, int32_t return Status::fromExceptionCode(Status::EX_SECURITY, "Only system uid can call informOnePackage"); } - m_UidMap->updateApp(app, uid, version); + mUidMap->updateApp(app, uid, version); return Status::ok(); } @@ -190,7 +334,7 @@ Status StatsService::informOnePackageRemoved(const String16& app, int32_t uid) { return Status::fromExceptionCode(Status::EX_SECURITY, "Only system uid can call informOnePackageRemoved"); } - m_UidMap->removeApp(app, uid); + mUidMap->removeApp(app, uid); return Status::ok(); } @@ -282,38 +426,18 @@ Status StatsService::statsCompanionReady() { "statscompanion unavailable despite it contacting statsd!"); } if (DEBUG) ALOGD("StatsService::statsCompanionReady linking to statsCompanion."); - IInterface::asBinder(statsCompanion)->linkToDeath(new StatsdDeathRecipient(mAnomalyMonitor)); + IInterface::asBinder(statsCompanion)->linkToDeath(new CompanionDeathRecipient(mAnomalyMonitor)); mAnomalyMonitor->setStatsCompanionService(statsCompanion); return Status::ok(); } -void StatsdDeathRecipient::binderDied(const wp<IBinder>& who) { - ALOGW("statscompanion service died"); - mAnmlyMntr->setStatsCompanionService(nullptr); -} - -status_t StatsService::doPrintStatsLog(FILE* out, const Vector<String8>& args) { - long msec = 0; - - if (args.size() > 2) { - msec = strtol(args[2].string(), NULL, 10); - } - return DropboxReader::readStatsLogs(out, args[1].string(), msec); -} - -status_t StatsService::doPrintUidMap(FILE* out) { - m_UidMap->printUidMap(out); - return NO_ERROR; +void StatsService::Startup() { + mConfigManager->Startup(); } -void StatsService::printCmdHelp(FILE* out) { - fprintf(out, "Usage:\n"); - fprintf(out, "\t print-stats-log [tag_required] [timestamp_nsec_optional]\n"); - fprintf(out, "\t print-uid-map Prints the UID, app name, version mapping.\n"); - fprintf(out, - "\t config\t Loads a new config from command-line (must be proto in wire-encoded " - "format).\n"); +void StatsService::OnLogEvent(const log_msg& msg) { + mProcessor->OnLogEvent(msg); } } // namespace statsd diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h index 541f7e8be7fa..dcc73a1d4788 100644 --- a/cmds/statsd/src/StatsService.h +++ b/cmds/statsd/src/StatsService.h @@ -17,11 +17,11 @@ #ifndef STATS_SERVICE_H #define STATS_SERVICE_H -#include "AnomalyMonitor.h" #include "StatsLogProcessor.h" -#include "StatsPullerManager.h" -#include "StatsPuller.h" -#include "UidMap.h" +#include "anomaly/AnomalyMonitor.h" +#include "config/ConfigManager.h" +#include "external/StatsPullerManager.h" +#include "packages/UidMap.h" #include <android/os/BnStatsManager.h> #include <android/os/IStatsCompanionService.h> @@ -42,75 +42,114 @@ namespace android { namespace os { namespace statsd { -class StatsService : public BnStatsManager { +class StatsService : public BnStatsManager, public LogListener { public: StatsService(const sp<Looper>& handlerLooper); virtual ~StatsService(); virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags); - virtual status_t dump(int fd, const Vector<String16>& args); - virtual status_t command(FILE* in, FILE* out, FILE* err, Vector<String8>& args); virtual Status systemRunning(); - - // Inform statsd that statsCompanion is ready. virtual Status statsCompanionReady(); - virtual Status informAnomalyAlarmFired(); - virtual Status informPollAlarmFired(); - virtual Status informAllUidData(const vector<int32_t>& uid, const vector<int32_t>& version, const vector<String16>& app); virtual Status informOnePackage(const String16& app, int32_t uid, int32_t version); virtual Status informOnePackageRemoved(const String16& app, int32_t uid); - virtual status_t setProcessor(const sp<StatsLogProcessor>& main_processor); + /** + * Called right before we start processing events. + */ + void Startup(); + + /** + * Called by LogReader when there's a log event to process. + */ + virtual void OnLogEvent(const log_msg& msg); // TODO: public for testing since statsd doesn't run when system starts. Change to private // later. /** Inform statsCompanion that statsd is ready. */ virtual void sayHiToStatsCompanion(); - // TODO: Move this to a more logical file/class - // TODO: Should be private. Temporarily public for testing purposes only. - const sp<AnomalyMonitor> mAnomalyMonitor; - - sp<UidMap> getUidMap() { - return m_UidMap; - } - /** Fetches and returns the StatsCompanionService. */ static sp<IStatsCompanionService> getStatsCompanionService(); private: - sp<UidMap> m_UidMap; // Reference to the UID map needed for translating UID to app name/version. - - sp<StatsLogProcessor> m_processor; // Reference to the processor for updating configs. - - status_t doPrintStatsLog(FILE* out, const Vector<String8>& args); - - void printCmdHelp(FILE* out); - - status_t doLoadConfig(FILE* in); - + /** + * Load system properties at init. + */ + void init_system_properties(); + + /** + * Helper for loading system properties. + */ + static void init_build_type_callback(void* cookie, const char* name, const char* value, + uint32_t serial); + + /** + * Text output of dumpsys. + */ + void dump_impl(FILE* out); + + /** + * Print usage information for the commands + */ + void print_cmd_help(FILE* out); + + /** + * Handle the config sub-command. + */ + status_t cmd_config(FILE* in, FILE* out, FILE* err, Vector<String8>& args); + + /** + * Print the event log. + */ + status_t cmd_print_stats_log(FILE* out, const Vector<String8>& args); + + /** + * Print the mapping of uids to package names. + */ + status_t cmd_print_uid_map(FILE* out); + + /** + * Update a configuration. + */ + void set_config(int uid, const string& name, const StatsdConfig& config); + + /** + * Tracks the uid <--> package name mapping. + */ + sp<UidMap> mUidMap; + + /** + * Fetches external metrics. + * TODO: This should be an sp<> + */ StatsPullerManager mStatsPullerManager; - status_t doPrintUidMap(FILE* out); -}; + /** + * Tracks the configurations that have been passed to statsd. + */ + sp<ConfigManager> mConfigManager; -// --- StatsdDeathRecipient --- -class StatsdDeathRecipient : public IBinder::DeathRecipient { -public: - StatsdDeathRecipient(sp<AnomalyMonitor> anomalyMonitor) : mAnmlyMntr(anomalyMonitor) { - } + /** + * The metrics recorder. + */ + sp<StatsLogProcessor> mProcessor; - virtual void binderDied(const wp<IBinder>& who); + /** + * The anomaly detector. + */ + const sp<AnomalyMonitor> mAnomalyMonitor; -private: - const sp<AnomalyMonitor> mAnmlyMntr; + /** + * Whether this is an eng build. + */ + bool mEngBuild; }; } // namespace statsd diff --git a/cmds/statsd/src/AnomalyMonitor.cpp b/cmds/statsd/src/anomaly/AnomalyMonitor.cpp index 4fbbc7a2267f..7a464104fb09 100644 --- a/cmds/statsd/src/AnomalyMonitor.cpp +++ b/cmds/statsd/src/anomaly/AnomalyMonitor.cpp @@ -14,12 +14,10 @@ * limitations under the License. */ -#define LOG_TAG "AnomalyMonitor" #define DEBUG true +#include "Log.h" -#include "AnomalyMonitor.h" - -#include <cutils/log.h> +#include "anomaly/AnomalyMonitor.h" namespace android { namespace os { @@ -92,17 +90,16 @@ void AnomalyMonitor::remove(sp<const AnomalyAlarm> alarm) { // More efficient than repeatedly calling remove(mPq.top()) since it batches the // updates to the registered alarm. -unordered_set<sp<const AnomalyAlarm>, SpHash<AnomalyAlarm>> - AnomalyMonitor::popSoonerThan(uint32_t timestampSec) { - +unordered_set<sp<const AnomalyAlarm>, SpHash<AnomalyAlarm>> AnomalyMonitor::popSoonerThan( + uint32_t timestampSec) { if (DEBUG) ALOGD("Removing alarms with time <= %u", timestampSec); unordered_set<sp<const AnomalyAlarm>, SpHash<AnomalyAlarm>> oldAlarms; std::lock_guard<std::mutex> lock(mLock); - for (sp<const AnomalyAlarm> t = mPq.top(); - t != nullptr && t->timestampSec <= timestampSec; t = mPq.top()) { + for (sp<const AnomalyAlarm> t = mPq.top(); t != nullptr && t->timestampSec <= timestampSec; + t = mPq.top()) { oldAlarms.insert(t); - mPq.pop(); // remove t + mPq.pop(); // remove t } // Always update registered alarm time (if anything has changed). if (!oldAlarms.empty()) { diff --git a/cmds/statsd/src/AnomalyMonitor.h b/cmds/statsd/src/anomaly/AnomalyMonitor.h index 7c6e5e8945a7..e2ac623c4670 100644 --- a/cmds/statsd/src/AnomalyMonitor.h +++ b/cmds/statsd/src/anomaly/AnomalyMonitor.h @@ -17,12 +17,13 @@ #ifndef ANOMALY_MONITOR_H #define ANOMALY_MONITOR_H +#include "anomaly/indexed_priority_queue.h" + #include <android/os/IStatsCompanionService.h> -#include <indexed_priority_queue.h> #include <utils/RefBase.h> -#include <unordered_set> #include <queue> +#include <unordered_set> #include <vector> using namespace android; @@ -91,8 +92,8 @@ public: * Returns and removes all alarms whose timestamp <= the given timestampSec. * Always updates the registered alarm if return is non-empty. */ - unordered_set<sp<const AnomalyAlarm>, SpHash<AnomalyAlarm>> - popSoonerThan(uint32_t timestampSec); + unordered_set<sp<const AnomalyAlarm>, SpHash<AnomalyAlarm>> popSoonerThan( + uint32_t timestampSec); /** * Returns the projected alarm timestamp that is registered with @@ -144,4 +145,4 @@ private: } // namespace os } // namespace android -#endif // ANOMALY_MONITOR_H
\ No newline at end of file +#endif // ANOMALY_MONITOR_H diff --git a/cmds/statsd/src/indexed_priority_queue.h b/cmds/statsd/src/anomaly/indexed_priority_queue.h index 81e8b3d023ea..1a2e9c2a41ed 100644 --- a/cmds/statsd/src/indexed_priority_queue.h +++ b/cmds/statsd/src/anomaly/indexed_priority_queue.h @@ -14,15 +14,10 @@ * limitations under the License. */ -#ifndef STATSD_INDEXED_PRIORITY_QUEUE_H -#define STATSD_INDEXED_PRIORITY_QUEUE_H +#pragma once -// ALOGE can be called from this file. If header loaded by another class, use their LOG_TAG instead. -#ifndef LOG_TAG -#define LOG_TAG "statsd(indexed_priority_queue)" -#endif // LOG_TAG +#include "Log.h" -#include <cutils/log.h> #include <utils/RefBase.h> #include <unordered_map> #include <vector> @@ -132,23 +127,23 @@ void indexed_priority_queue<AA, Comparator>::remove(sp<const AA> a) { // The same as, but slightly more efficient than, remove(top()). template <class AA, class Comparator> void indexed_priority_queue<AA, Comparator>::pop() { - sp<const AA> a = top(); - if (a == nullptr) return; - const size_t idx = 1; - if (idx == size()) { // if a is the last element + sp<const AA> a = top(); + if (a == nullptr) return; + const size_t idx = 1; + if (idx == size()) { // if a is the last element + pq.pop_back(); + indices.erase(a); + return; + } + // move last element (guaranteed not to be at idx) to idx, then delete a + sp<const AA> last_a = pq.back(); + pq[idx] = last_a; pq.pop_back(); + indices[last_a] = idx; indices.erase(a); - return; - } - // move last element (guaranteed not to be at idx) to idx, then delete a - sp<const AA> last_a = pq.back(); - pq[idx] = last_a; - pq.pop_back(); - indices[last_a] = idx; - indices.erase(a); - - // get the heap back in order (since the element at idx is not in order) - sift_down(idx); + + // get the heap back in order (since the element at idx is not in order) + sift_down(idx); } template <class AA, class Comparator> @@ -227,5 +222,3 @@ void indexed_priority_queue<AA, Comparator>::swap_indices(size_t i, size_t j) { } // namespace statsd } // namespace os } // namespace android - -#endif // STATSD_INDEXED_PRIORITY_QUEUE_H diff --git a/cmds/statsd/src/condition/CombinationConditionTracker.cpp b/cmds/statsd/src/condition/CombinationConditionTracker.cpp index 6188383d4e8d..c3a06bcbf971 100644 --- a/cmds/statsd/src/condition/CombinationConditionTracker.cpp +++ b/cmds/statsd/src/condition/CombinationConditionTracker.cpp @@ -13,23 +13,23 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#define LOG_TAG "CombinationConditionTracker" + #define DEBUG true // STOPSHIP if true -#define VLOG(...) \ - if (DEBUG) ALOGD(__VA_ARGS__); +#include "Log.h" #include "CombinationConditionTracker.h" -#include <cutils/log.h> + #include <log/logprint.h> -using std::string; -using std::unique_ptr; -using std::unordered_map; -using std::vector; namespace android { namespace os { namespace statsd { +using std::string; +using std::unique_ptr; +using std::unordered_map; +using std::vector; + CombinationConditionTracker::CombinationConditionTracker(const string& name, const int index) : ConditionTracker(name, index) { VLOG("creating CombinationConditionTracker %s", mName.c_str()); diff --git a/cmds/statsd/src/condition/ConditionTracker.h b/cmds/statsd/src/condition/ConditionTracker.h index 2da8fa0e8655..9554e0a894ef 100644 --- a/cmds/statsd/src/condition/ConditionTracker.h +++ b/cmds/statsd/src/condition/ConditionTracker.h @@ -14,17 +14,19 @@ * limitations under the License. */ -#ifndef CONDITION_TRACKER_H -#define CONDITION_TRACKER_H +#pragma once + +#include "Log.h" + +#include "condition/condition_util.h" +#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" +#include "matchers/LogMatchingTracker.h" +#include "matchers/matcher_util.h" -#include <cutils/log.h> #include <log/logprint.h> #include <utils/RefBase.h> + #include <unordered_map> -#include "../matchers/LogMatchingTracker.h" -#include "../matchers/matcher_util.h" -#include "condition_util.h" -#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" namespace android { namespace os { @@ -102,4 +104,3 @@ protected: } // namespace os } // namespace android -#endif // CONDITION_TRACKER_H diff --git a/cmds/statsd/src/condition/SimpleConditionTracker.cpp b/cmds/statsd/src/condition/SimpleConditionTracker.cpp index e78c0de7bdf5..694908dbb6cb 100644 --- a/cmds/statsd/src/condition/SimpleConditionTracker.cpp +++ b/cmds/statsd/src/condition/SimpleConditionTracker.cpp @@ -14,24 +14,22 @@ * limitations under the License. */ -#define LOG_TAG "Stats_SimpleConditionTracker" #define DEBUG true // STOPSHIP if true -#define VLOG(...) \ - if (DEBUG) ALOGD(__VA_ARGS__); +#include "Log.h" #include "SimpleConditionTracker.h" -#include <cutils/log.h> + #include <log/logprint.h> +namespace android { +namespace os { +namespace statsd { + using std::string; using std::unique_ptr; using std::unordered_map; using std::vector; -namespace android { -namespace os { -namespace statsd { - SimpleConditionTracker::SimpleConditionTracker( const string& name, const int index, const SimpleCondition& simpleCondition, const unordered_map<string, int>& trackerNameIndexMap) diff --git a/cmds/statsd/src/condition/condition_util.cpp b/cmds/statsd/src/condition/condition_util.cpp index cb07d1530dab..c7c8fcc9b361 100644 --- a/cmds/statsd/src/condition/condition_util.cpp +++ b/cmds/statsd/src/condition/condition_util.cpp @@ -14,9 +14,10 @@ * limitations under the License. */ +#include "Log.h" + #include "condition_util.h" -#include <cutils/log.h> #include <log/event_tag_map.h> #include <log/log_event_list.h> #include <log/logprint.h> @@ -27,14 +28,15 @@ #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" #include "stats_util.h" +namespace android { +namespace os { +namespace statsd { + using std::set; using std::string; using std::unordered_map; using std::vector; -namespace android { -namespace os { -namespace statsd { ConditionState evaluateCombinationCondition(const std::vector<int>& children, const LogicalOperation& operation, diff --git a/cmds/statsd/src/config/ConfigKey.cpp b/cmds/statsd/src/config/ConfigKey.cpp new file mode 100644 index 000000000000..a365dc0b9189 --- /dev/null +++ b/cmds/statsd/src/config/ConfigKey.cpp @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "config/ConfigKey.h" + +#include <sstream> + +namespace android { +namespace os { +namespace statsd { + +using std::ostringstream; + +ConfigKey::ConfigKey() { +} + +ConfigKey::ConfigKey(const ConfigKey& that) : mName(that.mName), mUid(that.mUid) { +} + +ConfigKey::ConfigKey(int uid, const string& name) : mName(name), mUid(uid) { +} + +ConfigKey::~ConfigKey() { +} + +string ConfigKey::ToString() const { + ostringstream out; + out << '(' << mUid << ',' << mName << ')'; + return out.str(); +} + +} // namespace statsd +} // namespace os +} // namespace android diff --git a/cmds/statsd/src/config/ConfigKey.h b/cmds/statsd/src/config/ConfigKey.h new file mode 100644 index 000000000000..bbf20fd1acf7 --- /dev/null +++ b/cmds/statsd/src/config/ConfigKey.h @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" + +#include <functional> +#include <iostream> +#include <string> + +namespace android { +namespace os { +namespace statsd { + +using std::hash; +using std::ostream; +using std::string; + +/** + * Uniquely identifies a configuration. + */ +class ConfigKey { +public: + ConfigKey(); + explicit ConfigKey(const ConfigKey& that); + ConfigKey(int uid, const string& name); + ~ConfigKey(); + + inline int GetUid() const { + return mUid; + } + inline const string& GetName() const { + return mName; + } + + inline bool operator<(const ConfigKey& that) const { + if (mUid < that.mUid) { + return true; + } + if (mUid > that.mUid) { + return false; + } + return mName < that.mName; + }; + + inline bool operator==(const ConfigKey& that) const { + return mUid == that.mUid && mName == that.mName; + }; + + string ToString() const; + +private: + string mName; + int mUid; +}; + +inline ostream& operator<<(ostream& os, const ConfigKey& config) { + return os << config.ToString(); +} + +} // namespace statsd +} // namespace os +} // namespace android + +/** + * A hash function for ConfigKey so it can be used for unordered_map/set. + * Unfortunately this hast to go in std namespace because C++ is fun! + */ +namespace std { + +using android::os::statsd::ConfigKey; + +template <> +struct hash<ConfigKey> { + std::size_t operator()(const ConfigKey& key) const { + return (7 * key.GetUid()) ^ ((hash<string>()(key.GetName()))); + } +}; + +} // namespace std diff --git a/cmds/statsd/src/config/ConfigListener.cpp b/cmds/statsd/src/config/ConfigListener.cpp new file mode 100644 index 000000000000..21a3f1673fd7 --- /dev/null +++ b/cmds/statsd/src/config/ConfigListener.cpp @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "config/ConfigListener.h" + +namespace android { +namespace os { +namespace statsd { + +ConfigListener::ConfigListener() { +} + +ConfigListener::~ConfigListener() { +} + +} // namespace statsd +} // namespace os +} // namespace android diff --git a/cmds/statsd/src/config/ConfigListener.h b/cmds/statsd/src/config/ConfigListener.h new file mode 100644 index 000000000000..a58766d2a382 --- /dev/null +++ b/cmds/statsd/src/config/ConfigListener.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <frameworks/base/cmds/statsd/src/stats_log.pb.h> +#include "config/ConfigKey.h" + +#include <utils/RefBase.h> +#include <string> + +namespace android { +namespace os { +namespace statsd { + +using android::RefBase; +using std::string; + +/** + * Callback for different subsystems inside statsd to implement to find out + * when a configuration has been added, updated or removed. + */ +class ConfigListener : public virtual RefBase { +public: + ConfigListener(); + virtual ~ConfigListener(); + + /** + * A configuration was added or updated. + */ + virtual void OnConfigUpdated(const ConfigKey& key, const StatsdConfig& config) = 0; + + /** + * A configuration was removed. + */ + virtual void OnConfigRemoved(const ConfigKey& key) = 0; +}; + +} // namespace statsd +} // namespace os +} // namespace android diff --git a/cmds/statsd/src/config/ConfigManager.cpp b/cmds/statsd/src/config/ConfigManager.cpp new file mode 100644 index 000000000000..2a4d6e22a2ed --- /dev/null +++ b/cmds/statsd/src/config/ConfigManager.cpp @@ -0,0 +1,251 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "config/ConfigManager.h" + +#include "stats_util.h" + +#include <vector> + +#include <stdio.h> + +namespace android { +namespace os { +namespace statsd { + +static StatsdConfig build_fake_config(); + +ConfigManager::ConfigManager() { +} + +ConfigManager::~ConfigManager() { +} + +void ConfigManager::Startup() { + // TODO: Implement me -- read from storage and call onto all of the listeners. + // Instead, we'll just make a fake one. + + // this should be called from StatsService when it receives a statsd_config + UpdateConfig(ConfigKey(0, "fake"), build_fake_config()); +} + +void ConfigManager::AddListener(const sp<ConfigListener>& listener) { + mListeners.push_back(listener); +} + +void ConfigManager::UpdateConfig(const ConfigKey& key, const StatsdConfig& config) { + // Add to map + mConfigs[key] = config; + // Why doesn't this work? mConfigs.insert({key, config}); + + // Save to disk + update_saved_configs(); + + // Tell everyone + for (auto& listener : mListeners) { + listener->OnConfigUpdated(key, config); + } +} + +void ConfigManager::RemoveConfig(const ConfigKey& key) { + unordered_map<ConfigKey, StatsdConfig>::iterator it = mConfigs.find(key); + if (it != mConfigs.end()) { + // Remove from map + mConfigs.erase(it); + + // Save to disk + update_saved_configs(); + + // Tell everyone + for (auto& listener : mListeners) { + listener->OnConfigRemoved(key); + } + } + // If we didn't find it, just quietly ignore it. +} + +void ConfigManager::RemoveConfigs(int uid) { + vector<ConfigKey> removed; + + for (auto it = mConfigs.begin(); it != mConfigs.end();) { + // Remove from map + if (it->first.GetUid() == uid) { + removed.push_back(it->first); + it = mConfigs.erase(it); + } else { + it++; + } + } + + // Remove separately so if they do anything in the callback they can't mess up our iteration. + for (auto& key : removed) { + // Tell everyone + for (auto& listener : mListeners) { + listener->OnConfigRemoved(key); + } + } +} + +void ConfigManager::Dump(FILE* out) { + fprintf(out, "CONFIGURATIONS (%d)\n", (int)mConfigs.size()); + fprintf(out, " uid name\n"); + for (unordered_map<ConfigKey, StatsdConfig>::const_iterator it = mConfigs.begin(); + it != mConfigs.end(); it++) { + fprintf(out, " %6d %s\n", it->first.GetUid(), it->first.GetName().c_str()); + // TODO: Print the contents of the config too. + } +} + +void ConfigManager::update_saved_configs() { + // TODO: Implement me -- write to disk. +} + +static StatsdConfig build_fake_config() { + // HACK: Hard code a test metric for counting screen on events... + StatsdConfig config; + config.set_config_id(12345L); + + // One count metric to count screen on + CountMetric* metric = config.add_count_metric(); + metric->set_metric_id(20150717L); + metric->set_what("SCREEN_IS_ON"); + metric->mutable_bucket()->set_bucket_size_millis(30 * 1000L); + + // One count metric to count PHOTO_CHANGE_OR_CHROME_CRASH + metric = config.add_count_metric(); + metric->set_metric_id(20150718L); + metric->set_what("PHOTO_PROCESS_STATE_CHANGE"); + metric->mutable_bucket()->set_bucket_size_millis(60 * 1000L); + metric->set_condition("SCREEN_IS_ON"); + + LogEntryMatcher* eventMatcher = config.add_log_entry_matcher(); + eventMatcher->set_name("SCREEN_IS_ON"); + + SimpleLogEntryMatcher* simpleLogEntryMatcher = eventMatcher->mutable_simple_log_entry_matcher(); + simpleLogEntryMatcher->add_tag(2 /*SCREEN_STATE_CHANGE*/); + simpleLogEntryMatcher->add_key_value_matcher()->mutable_key_matcher()->set_key( + 1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/); + simpleLogEntryMatcher->mutable_key_value_matcher(0)->set_eq_int( + 2 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_ON*/); + + eventMatcher = config.add_log_entry_matcher(); + eventMatcher->set_name("SCREEN_IS_OFF"); + + simpleLogEntryMatcher = eventMatcher->mutable_simple_log_entry_matcher(); + simpleLogEntryMatcher->add_tag(2 /*SCREEN_STATE_CHANGE*/); + simpleLogEntryMatcher->add_key_value_matcher()->mutable_key_matcher()->set_key( + 1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/); + simpleLogEntryMatcher->mutable_key_value_matcher(0)->set_eq_int( + 1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_OFF*/); + + LogEntryMatcher* procEventMatcher = config.add_log_entry_matcher(); + procEventMatcher->set_name("PHOTO_CRASH"); + + SimpleLogEntryMatcher* simpleLogMatcher2 = procEventMatcher->mutable_simple_log_entry_matcher(); + simpleLogMatcher2->add_tag(1112 /*PROCESS_STATE_CHANGE*/); + KeyValueMatcher* keyValueMatcher = simpleLogMatcher2->add_key_value_matcher(); + keyValueMatcher->mutable_key_matcher()->set_key(1002 /*pkg*/); + keyValueMatcher->set_eq_string( + "com.google.android.apps.photos" /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_ON*/); + + keyValueMatcher = simpleLogMatcher2->add_key_value_matcher(); + keyValueMatcher->mutable_key_matcher()->set_key(1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/); + keyValueMatcher->set_eq_int(2); + + procEventMatcher = config.add_log_entry_matcher(); + procEventMatcher->set_name("PHOTO_START"); + + simpleLogMatcher2 = procEventMatcher->mutable_simple_log_entry_matcher(); + simpleLogMatcher2->add_tag(1112 /*PROCESS_STATE_CHANGE*/); + keyValueMatcher = simpleLogMatcher2->add_key_value_matcher(); + keyValueMatcher->mutable_key_matcher()->set_key(1002 /*pkg*/); + keyValueMatcher->set_eq_string( + "com.google.android.apps.photos" /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_ON*/); + + keyValueMatcher = simpleLogMatcher2->add_key_value_matcher(); + keyValueMatcher->mutable_key_matcher()->set_key(1 /*STATE*/); + keyValueMatcher->set_eq_int(1); + + procEventMatcher = config.add_log_entry_matcher(); + procEventMatcher->set_name("PHOTO_PROCESS_STATE_CHANGE"); + LogEntryMatcher_Combination* combinationMatcher = procEventMatcher->mutable_combination(); + combinationMatcher->set_operation(LogicalOperation::OR); + combinationMatcher->add_matcher("PHOTO_START"); + combinationMatcher->add_matcher("PHOTO_CRASH"); + + procEventMatcher = config.add_log_entry_matcher(); + procEventMatcher->set_name("CHROME_CRASH"); + + simpleLogMatcher2 = procEventMatcher->mutable_simple_log_entry_matcher(); + simpleLogMatcher2->add_tag(1112 /*PROCESS_STATE_CHANGE*/); + keyValueMatcher = simpleLogMatcher2->add_key_value_matcher(); + keyValueMatcher->mutable_key_matcher()->set_key(1002 /*pkg*/); + keyValueMatcher->set_eq_string( + "com.android.chrome" /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_ON*/); + + keyValueMatcher = simpleLogMatcher2->add_key_value_matcher(); + keyValueMatcher->mutable_key_matcher()->set_key(1 /*STATE*/); + keyValueMatcher->set_eq_int(2); + + procEventMatcher = config.add_log_entry_matcher(); + procEventMatcher->set_name("PHOTO_CHANGE_OR_CHROME_CRASH"); + combinationMatcher = procEventMatcher->mutable_combination(); + combinationMatcher->set_operation(LogicalOperation::OR); + combinationMatcher->add_matcher("PHOTO_PROCESS_STATE_CHANGE"); + combinationMatcher->add_matcher("CHROME_CRASH"); + + Condition* condition = config.add_condition(); + condition->set_name("SCREEN_IS_ON"); + SimpleCondition* simpleCondition = condition->mutable_simple_condition(); + simpleCondition->set_start("SCREEN_IS_ON"); + simpleCondition->set_stop("SCREEN_IS_OFF"); + + condition = config.add_condition(); + condition->set_name("PHOTO_STARTED"); + + simpleCondition = condition->mutable_simple_condition(); + simpleCondition->set_start("PHOTO_START"); + simpleCondition->set_stop("PHOTO_CRASH"); + + condition = config.add_condition(); + condition->set_name("SCREEN_IS_OFF"); + + simpleCondition = condition->mutable_simple_condition(); + simpleCondition->set_start("SCREEN_IS_OFF"); + simpleCondition->set_stop("SCREEN_IS_ON"); + + condition = config.add_condition(); + condition->set_name("SCREEN_IS_EITHER_ON_OFF"); + + Condition_Combination* combination = condition->mutable_combination(); + combination->set_operation(LogicalOperation::OR); + combination->add_condition("SCREEN_IS_ON"); + combination->add_condition("SCREEN_IS_OFF"); + + condition = config.add_condition(); + condition->set_name("SCREEN_IS_NEITHER_ON_OFF"); + + combination = condition->mutable_combination(); + combination->set_operation(LogicalOperation::NOR); + combination->add_condition("SCREEN_IS_ON"); + combination->add_condition("SCREEN_IS_OFF"); + + return config; +} + +} // namespace statsd +} // namespace os +} // namespace android diff --git a/cmds/statsd/src/config/ConfigManager.h b/cmds/statsd/src/config/ConfigManager.h new file mode 100644 index 000000000000..5d73eaf3c7b6 --- /dev/null +++ b/cmds/statsd/src/config/ConfigManager.h @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "config/ConfigKey.h" +#include "config/ConfigListener.h" + +#include <string> +#include <unordered_map> + +#include <stdio.h> + +namespace android { +namespace os { +namespace statsd { + +using android::RefBase; +using std::string; +using std::unordered_map; +using std::vector; + +/** + * Keeps track of which configurations have been set from various sources. + * + * TODO: Store the configs persistently too. + * TODO: Dump method for debugging. + */ +class ConfigManager : public virtual RefBase { +public: + ConfigManager(); + virtual ~ConfigManager(); + + /** + * Call to load the saved configs from disk. + * + * TODO: Implement me + */ + void Startup(); + + /** + * Someone else wants to know about the configs. + */ + void AddListener(const sp<ConfigListener>& listener); + + /** + * A configuration was added or updated. + * + * Reports this to listeners. + */ + void UpdateConfig(const ConfigKey& key, const StatsdConfig& data); + + /** + * A configuration was removed. + * + * Reports this to listeners. + */ + void RemoveConfig(const ConfigKey& key); + + /** + * Remove all of the configs for the given uid. + */ + void RemoveConfigs(int uid); + + /** + * Text dump of our state for debugging. + */ + void Dump(FILE* out); + +private: + /** + * Save the configs to disk. + */ + void update_saved_configs(); + + /** + * The Configs that have been set + */ + unordered_map<ConfigKey, StatsdConfig> mConfigs; + + /** + * The ConfigListeners that will be told about changes. + */ + vector<sp<ConfigListener>> mListeners; +}; + +} // namespace statsd +} // namespace os +} // namespace android diff --git a/cmds/statsd/src/KernelWakelockPuller.cpp b/cmds/statsd/src/external/KernelWakelockPuller.cpp index 1798f9dec5ef..b9abee0d5df1 100644 --- a/cmds/statsd/src/KernelWakelockPuller.cpp +++ b/cmds/statsd/src/external/KernelWakelockPuller.cpp @@ -14,13 +14,14 @@ * limitations under the License. */ -#include "KernelWakelockPuller.h" +#include "Log.h" + #include <android/os/IStatsCompanionService.h> #include <binder/IPCThreadState.h> -#include <cutils/log.h> #include <private/android_filesystem_config.h> -#include "StatsPuller.h" #include "StatsService.h" +#include "external/KernelWakelockPuller.h" +#include "external/StatsPuller.h" using namespace android; using namespace android::base; @@ -40,15 +41,15 @@ String16 KernelWakelockPuller::pull() { sp<IStatsCompanionService> statsCompanion = StatsService::getStatsCompanionService(); String16 returned_value(""); if (statsCompanion != NULL) { - Status status = statsCompanion->pullData(KernelWakelockPuller::PULL_CODE_KERNEL_WAKELOCKS, - &returned_value); - if (!status.isOk()) { - ALOGW("error pulling kernel wakelock"); - } - ALOGD("KernelWakelockPuller::pull succeeded!"); - // TODO: remove this when we integrate into aggregation chain. - ALOGD("%s", String8(returned_value).string()); - return returned_value; + Status status = statsCompanion->pullData(KernelWakelockPuller::PULL_CODE_KERNEL_WAKELOCKS, + &returned_value); + if (!status.isOk()) { + ALOGW("error pulling kernel wakelock"); + } + ALOGD("KernelWakelockPuller::pull succeeded!"); + // TODO: remove this when we integrate into aggregation chain. + ALOGD("%s", String8(returned_value).string()); + return returned_value; } else { ALOGW("statsCompanion not found!"); return String16(); diff --git a/cmds/statsd/src/KernelWakelockPuller.h b/cmds/statsd/src/external/KernelWakelockPuller.h index 1c16f8703082..1ec33762bd49 100644 --- a/cmds/statsd/src/KernelWakelockPuller.h +++ b/cmds/statsd/src/external/KernelWakelockPuller.h @@ -18,7 +18,7 @@ #define STATSD_KERNELWAKELOCKPULLER_H #include <utils/String16.h> -#include "StatsPuller.h" +#include "external/StatsPuller.h" namespace android { namespace os { diff --git a/cmds/statsd/src/StatsPuller.h b/cmds/statsd/src/external/StatsPuller.h index 5e556b89b521..5e556b89b521 100644 --- a/cmds/statsd/src/StatsPuller.h +++ b/cmds/statsd/src/external/StatsPuller.h diff --git a/cmds/statsd/src/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp index f4cf1ceaf18a..6e8d58bc33ad 100644 --- a/cmds/statsd/src/StatsPullerManager.cpp +++ b/cmds/statsd/src/external/StatsPullerManager.cpp @@ -14,15 +14,13 @@ * limitations under the License. */ -#define LOG_TAG "StatsPullerManager" #define DEBUG true +#include "Log.h" -#include "StatsPullerManager.h" #include <android/os/IStatsCompanionService.h> -#include <cutils/log.h> -#include "StatsService.h" #include "KernelWakelockPuller.h" - +#include "StatsService.h" +#include "external/StatsPullerManager.h" using namespace android; diff --git a/cmds/statsd/src/StatsPullerManager.h b/cmds/statsd/src/external/StatsPullerManager.h index ab36df535ae5..f143424e7b76 100644 --- a/cmds/statsd/src/StatsPullerManager.h +++ b/cmds/statsd/src/external/StatsPullerManager.h @@ -19,7 +19,7 @@ #include <utils/String16.h> #include <unordered_map> -#include "StatsPuller.h" +#include "external/StatsPuller.h" namespace android { namespace os { @@ -41,7 +41,6 @@ private: std::unordered_map<int, std::unique_ptr<StatsPuller>> mStatsPullers; }; - } // namespace statsd } // namespace os } // namespace android diff --git a/cmds/statsd/src/logd/LogListener.cpp b/cmds/statsd/src/logd/LogListener.cpp new file mode 100644 index 000000000000..6ac7978bbac9 --- /dev/null +++ b/cmds/statsd/src/logd/LogListener.cpp @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "logd/LogReader.h" + +#include <log/log_read.h> + +#include <utils/Errors.h> + +#include <time.h> +#include <unistd.h> + +using namespace android; +using namespace std; + +namespace android { +namespace os { +namespace statsd { + +LogListener::LogListener() { +} + +LogListener::~LogListener() { +} + +} // namespace statsd +} // namespace os +} // namespace android diff --git a/cmds/statsd/src/LogEntryPrinter.h b/cmds/statsd/src/logd/LogListener.h index 4f79028889c9..786a79305296 100644 --- a/cmds/statsd/src/LogEntryPrinter.h +++ b/cmds/statsd/src/logd/LogListener.h @@ -14,48 +14,29 @@ * limitations under the License. */ -#ifndef LOG_ENTRY_PRINTER_H -#define LOG_ENTRY_PRINTER_H +#pragma once -#include "LogReader.h" - -#include <log/logprint.h> - -#include <stdio.h> +#include <log/log_read.h> +#include <utils/RefBase.h> +#include <vector> namespace android { namespace os { namespace statsd { /** - * Decodes the log entry and prints it to the supplied file descriptor. + * Callback for LogReader */ -class LogEntryPrinter : public LogListener { +class LogListener : public virtual android::RefBase { public: - LogEntryPrinter(int out); - virtual ~LogEntryPrinter(); - - virtual void OnLogEvent(const log_msg& msg); + LogListener(); + virtual ~LogListener(); -private: - /** - * Where to write to. - */ - int m_out; - - /** - * Numeric to string tag name mapping. - */ - EventTagMap* m_tags; - - /** - * Pretty printing format. - */ - AndroidLogFormat* m_format; + // TODO: Rather than using log_msg, which doesn't have any real internal structure + // here, we should pull this out into our own LogEntry class. + virtual void OnLogEvent(const log_msg& msg) = 0; }; } // namespace statsd } // namespace os } // namespace android - -#endif // LOG_ENTRY_PRINTER_H diff --git a/cmds/statsd/src/LogReader.cpp b/cmds/statsd/src/logd/LogReader.cpp index c4ac33724bc7..23d3698aff0c 100644 --- a/cmds/statsd/src/LogReader.cpp +++ b/cmds/statsd/src/logd/LogReader.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "LogReader.h" +#include "logd/LogReader.h" #include <log/log_read.h> @@ -33,24 +33,12 @@ namespace statsd { #define SNOOZE_INITIAL_MS 100 #define SNOOZE_MAX_MS (10 * 60 * 1000) // Ten minutes -// ================================================================================ -LogListener::LogListener() { -} - -LogListener::~LogListener() { -} - -// ================================================================================ -LogReader::LogReader() { +LogReader::LogReader(const sp<LogListener>& listener) : mListener(listener) { } LogReader::~LogReader() { } -void LogReader::AddListener(const sp<LogListener>& listener) { - m_listeners.push_back(listener); -} - void LogReader::Run() { int nextSnoozeMs = SNOOZE_INITIAL_MS; @@ -119,11 +107,8 @@ int LogReader::connect_and_read() { // Record that we read one (used above to know how to snooze). lineCount++; - // Call the listeners - for (vector<sp<LogListener> >::iterator it = m_listeners.begin(); - it != m_listeners.end(); it++) { - (*it)->OnLogEvent(msg); - } + // Call the listener + mListener->OnLogEvent(msg); } } diff --git a/cmds/statsd/src/LogReader.h b/cmds/statsd/src/logd/LogReader.h index fc19585ac6f0..6ca0646eae6b 100644 --- a/cmds/statsd/src/LogReader.h +++ b/cmds/statsd/src/logd/LogReader.h @@ -17,6 +17,8 @@ #ifndef LOGREADER_H #define LOGREADER_H +#include "logd/LogListener.h" + #include <log/log_read.h> #include <utils/RefBase.h> @@ -27,27 +29,14 @@ namespace os { namespace statsd { /** - * Callback for LogReader - */ -class LogListener : public virtual android::RefBase { -public: - LogListener(); - virtual ~LogListener(); - - // TODO: Rather than using log_msg, which doesn't have any real internal structure - // here, we should pull this out into our own LogEntry class. - virtual void OnLogEvent(const log_msg& msg) = 0; -}; - -/** * Class to read logs from logd. */ class LogReader : public virtual android::RefBase { public: /** - * Construct the LogReader with a pointer back to the StatsService + * Construct the LogReader with the event listener. (Which is StatsService) */ - LogReader(); + LogReader(const sp<LogListener>& listener); /** * Destructor. @@ -55,20 +44,15 @@ public: virtual ~LogReader(); /** - * Add a LogListener class. - */ - void AddListener(const android::sp<LogListener>& listener); - - /** * Run the main LogReader loop */ void Run(); private: /** - * List of listeners to call back on when we do get an event. + * Who is going to get the events when they're read. */ - std::vector<android::sp<LogListener> > m_listeners; + sp<LogListener> mListener; /** * Connect to a single instance of logd, and read until there's a read error. diff --git a/cmds/statsd/src/main.cpp b/cmds/statsd/src/main.cpp index 37477dc50e4e..a7402800630e 100644 --- a/cmds/statsd/src/main.cpp +++ b/cmds/statsd/src/main.cpp @@ -14,20 +14,16 @@ * limitations under the License. */ -#define LOG_TAG "statsd" +#include "Log.h" -#include "LogEntryPrinter.h" -#include "LogReader.h" -#include "StatsLogProcessor.h" #include "StatsService.h" -#include "UidMap.h" +#include "logd/LogReader.h" #include <binder/IInterface.h> #include <binder/IPCThreadState.h> #include <binder/IServiceManager.h> #include <binder/ProcessState.h> #include <binder/Status.h> -#include <cutils/log.h> #include <utils/Looper.h> #include <utils/StrongPointer.h> @@ -53,16 +49,12 @@ struct log_reader_thread_data { static void* log_reader_thread_func(void* cookie) { log_reader_thread_data* data = static_cast<log_reader_thread_data*>(cookie); - sp<LogReader> reader = new LogReader(); + sp<LogReader> reader = new LogReader(data->service); - // Put the printer one first, so it will print before the real ones. - reader->AddListener(new LogEntryPrinter(STDOUT_FILENO)); - sp<StatsLogProcessor> main_processor = new StatsLogProcessor(data->service->getUidMap()); - data->service->setProcessor(main_processor); - reader->AddListener(main_processor); - - // TODO: Construct and add real LogListners here. + // Tell StatsService that we're ready to go. + data->service->Startup(); + // Run the read loop. Never returns. reader->Run(); ALOGW("statsd LogReader.Run() is not supposed to return."); @@ -127,6 +119,8 @@ int main(int /*argc*/, char** /*argv*/) { // TODO: This line is temporary, since statsd doesn't start up automatically (and therefore // the call in StatsService::SystemRunning() won't ever be called right now). + // TODO: Are you sure? Don't we need to reconnect to the system process if we get restarted? + // --joeo service->sayHiToStatsCompanion(); // Start the log reader thread diff --git a/cmds/statsd/src/matchers/CombinationLogMatchingTracker.cpp b/cmds/statsd/src/matchers/CombinationLogMatchingTracker.cpp index 9f9b648ae1a5..f1e0d5aed4c5 100644 --- a/cmds/statsd/src/matchers/CombinationLogMatchingTracker.cpp +++ b/cmds/statsd/src/matchers/CombinationLogMatchingTracker.cpp @@ -14,20 +14,21 @@ * limitations under the License. */ +#include "Log.h" + #include "CombinationLogMatchingTracker.h" +#include "matchers/matcher_util.h" + +namespace android { +namespace os { +namespace statsd { -#include <cutils/log.h> -#include "matcher_util.h" using std::set; using std::string; using std::unique_ptr; using std::unordered_map; using std::vector; -namespace android { -namespace os { -namespace statsd { - CombinationLogMatchingTracker::CombinationLogMatchingTracker(const string& name, const int index) : LogMatchingTracker(name, index) { } diff --git a/cmds/statsd/src/matchers/SimpleLogMatchingTracker.cpp b/cmds/statsd/src/matchers/SimpleLogMatchingTracker.cpp index 1c83039072da..815baf77e5a5 100644 --- a/cmds/statsd/src/matchers/SimpleLogMatchingTracker.cpp +++ b/cmds/statsd/src/matchers/SimpleLogMatchingTracker.cpp @@ -14,23 +14,22 @@ * limitations under the License. */ -#define LOG_TAG "SimpleLogMatchingTracker" #define DEBUG true // STOPSHIP if true -#define VLOG(...) \ - if (DEBUG) ALOGD(__VA_ARGS__); +#include "Log.h" #include "SimpleLogMatchingTracker.h" -#include <cutils/log.h> + #include <log/logprint.h> +namespace android { +namespace os { +namespace statsd { + using std::string; using std::unique_ptr; using std::unordered_map; using std::vector; -namespace android { -namespace os { -namespace statsd { SimpleLogMatchingTracker::SimpleLogMatchingTracker(const string& name, const int index, const SimpleLogEntryMatcher& matcher) diff --git a/cmds/statsd/src/matchers/matcher_util.cpp b/cmds/statsd/src/matchers/matcher_util.cpp index 3308f3aeb318..ce0576cc082b 100644 --- a/cmds/statsd/src/matchers/matcher_util.cpp +++ b/cmds/statsd/src/matchers/matcher_util.cpp @@ -14,19 +14,21 @@ * limitations under the License. */ -#include "matcher_util.h" -#include <cutils/log.h> +#include "Log.h" + +#include "frameworks/base/cmds/statsd/src/stats_log.pb.h" +#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" +#include "matchers/LogMatchingTracker.h" +#include "matchers/matcher_util.h" +#include "stats_util.h" + #include <log/event_tag_map.h> #include <log/log_event_list.h> #include <log/logprint.h> #include <utils/Errors.h> -#include <unordered_map> -#include "LogMatchingTracker.h" -#include "frameworks/base/cmds/statsd/src/stats_log.pb.h" -#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" -#include "stats_util.h" #include <sstream> +#include <unordered_map> using std::set; using std::string; diff --git a/cmds/statsd/src/metrics/CountAnomalyTracker.cpp b/cmds/statsd/src/metrics/CountAnomalyTracker.cpp index ebd53e056249..e1c2b8b3a90e 100644 --- a/cmds/statsd/src/metrics/CountAnomalyTracker.cpp +++ b/cmds/statsd/src/metrics/CountAnomalyTracker.cpp @@ -14,15 +14,14 @@ * limitations under the License. */ -#define LOG_TAG "CountAnomaly" #define DEBUG true // STOPSHIP if true +#include "Log.h" + #define VLOG(...) \ if (DEBUG) ALOGD(__VA_ARGS__); #include "CountAnomalyTracker.h" -#include <cutils/log.h> - namespace android { namespace os { namespace statsd { diff --git a/cmds/statsd/src/metrics/CountMetricProducer.cpp b/cmds/statsd/src/metrics/CountMetricProducer.cpp index e98999e73223..68d0a30403b0 100644 --- a/cmds/statsd/src/metrics/CountMetricProducer.cpp +++ b/cmds/statsd/src/metrics/CountMetricProducer.cpp @@ -14,10 +14,8 @@ * limitations under the License. */ -#define LOG_TAG "CountMetric" #define DEBUG true // STOPSHIP if true -#define VLOG(...) \ - if (DEBUG) ALOGD(__VA_ARGS__); +#include "Log.h" #include "CountMetricProducer.h" #include "CountAnomalyTracker.h" diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h index 6c39d98281ca..7bc7f978d782 100644 --- a/cmds/statsd/src/metrics/MetricProducer.h +++ b/cmds/statsd/src/metrics/MetricProducer.h @@ -17,10 +17,11 @@ #ifndef METRIC_PRODUCER_H #define METRIC_PRODUCER_H +#include "matchers/matcher_util.h" +#include "packages/PackageInfoListener.h" + #include <log/logprint.h> #include <utils/RefBase.h> -#include "../matchers/matcher_util.h" -#include "PackageInfoListener.h" namespace android { namespace os { diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp index 1e65f5888233..b77daf107503 100644 --- a/cmds/statsd/src/metrics/MetricsManager.cpp +++ b/cmds/statsd/src/metrics/MetricsManager.cpp @@ -13,13 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#define LOG_TAG "MetricManager" #define DEBUG true // STOPSHIP if true +#include "Log.h" #define VLOG(...) \ if (DEBUG) ALOGD(__VA_ARGS__); #include "MetricsManager.h" -#include <cutils/log.h> #include <log/logprint.h> #include "../condition/CombinationConditionTracker.h" #include "../condition/SimpleConditionTracker.h" diff --git a/cmds/statsd/src/metrics/MetricsManager.h b/cmds/statsd/src/metrics/MetricsManager.h index 70c34db6b80a..2f3fad930bd6 100644 --- a/cmds/statsd/src/metrics/MetricsManager.h +++ b/cmds/statsd/src/metrics/MetricsManager.h @@ -14,16 +14,15 @@ * limitations under the License. */ -#ifndef METRICS_MANAGER_H -#define METRICS_MANAGER_H +#pragma once + +#include "condition/ConditionTracker.h" +#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" +#include "matchers/LogMatchingTracker.h" +#include "metrics/MetricProducer.h" -#include <cutils/log.h> #include <log/logprint.h> #include <unordered_map> -#include "../condition/ConditionTracker.h" -#include "../matchers/LogMatchingTracker.h" -#include "MetricProducer.h" -#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" namespace android { namespace os { @@ -94,4 +93,3 @@ private: } // namespace os } // namespace android -#endif // METRICS_MANAGER_H diff --git a/cmds/statsd/src/PackageInfoListener.h b/cmds/statsd/src/packages/PackageInfoListener.h index 476c1d953cbc..8b948dee887e 100644 --- a/cmds/statsd/src/PackageInfoListener.h +++ b/cmds/statsd/src/packages/PackageInfoListener.h @@ -35,4 +35,4 @@ public: } // namespace os } // namespace android -#endif //STATSD_PACKAGE_INFO_LISTENER_H +#endif // STATSD_PACKAGE_INFO_LISTENER_H diff --git a/cmds/statsd/src/UidMap.cpp b/cmds/statsd/src/packages/UidMap.cpp index 76a7f3f28dee..f4621ee15dbe 100644 --- a/cmds/statsd/src/UidMap.cpp +++ b/cmds/statsd/src/packages/UidMap.cpp @@ -14,8 +14,10 @@ * limitations under the License. */ -#include "UidMap.h" -#include <cutils/log.h> +#include "Log.h" + +#include "packages/UidMap.h" + #include <utils/Errors.h> using namespace android; @@ -48,18 +50,18 @@ int UidMap::getAppVersion(int uid, const string& packageName) const { return 0; } -void UidMap::updateMap(const vector <int32_t> &uid, const vector <int32_t> &versionCode, - const vector <String16> &packageName) { - lock_guard<mutex> lock(mMutex); // Exclusively lock for updates. +void UidMap::updateMap(const vector<int32_t>& uid, const vector<int32_t>& versionCode, + const vector<String16>& packageName) { + lock_guard<mutex> lock(mMutex); // Exclusively lock for updates. mMap.clear(); - for (unsigned long j=0; j<uid.size(); j++) { - mMap.insert(make_pair(uid[j], AppData(string(String8(packageName[j]).string()), - versionCode[j]))); + for (unsigned long j = 0; j < uid.size(); j++) { + mMap.insert(make_pair(uid[j], + AppData(string(String8(packageName[j]).string()), versionCode[j]))); } - if (mOutput.initial_size() == 0) { // Provide the initial states in the mOutput proto - for (unsigned long j=0; j<uid.size(); j++) { + if (mOutput.initial_size() == 0) { // Provide the initial states in the mOutput proto + for (unsigned long j = 0; j < uid.size(); j++) { auto t = mOutput.add_initial(); t->set_app(string(String8(packageName[j]).string())); t->set_version(int(versionCode[j])); @@ -68,7 +70,7 @@ void UidMap::updateMap(const vector <int32_t> &uid, const vector <int32_t> &vers } } -void UidMap::updateApp(const String16& app_16, const int32_t& uid, const int32_t& versionCode){ +void UidMap::updateApp(const String16& app_16, const int32_t& uid, const int32_t& versionCode) { lock_guard<mutex> lock(mMutex); string app = string(String8(app_16).string()); @@ -80,7 +82,7 @@ void UidMap::updateApp(const String16& app_16, const int32_t& uid, const int32_t auto log = mOutput.add_changes(); log->set_deletion(false); - //log.timestamp = TODO: choose how timestamps are computed + // log.timestamp = TODO: choose how timestamps are computed log->set_app(app); log->set_uid(uid); log->set_version(versionCode); @@ -99,15 +101,14 @@ void UidMap::updateApp(const String16& app_16, const int32_t& uid, const int32_t mMap.insert(make_pair(uid, AppData(app, int(versionCode)))); } - -void UidMap::removeApp(const String16& app_16, const int32_t& uid){ +void UidMap::removeApp(const String16& app_16, const int32_t& uid) { lock_guard<mutex> lock(mMutex); string app = string(String8(app_16).string()); auto log = mOutput.add_changes(); log->set_deletion(true); - //log.timestamp = TODO: choose how timestamps are computed + // log.timestamp = TODO: choose how timestamps are computed log->set_app(app); log->set_uid(uid); @@ -123,19 +124,19 @@ void UidMap::removeApp(const String16& app_16, const int32_t& uid){ } void UidMap::addListener(sp<PackageInfoListener> producer) { - lock_guard<mutex> lock(mMutex); // Lock for updates + lock_guard<mutex> lock(mMutex); // Lock for updates mSubscribers.insert(producer); } void UidMap::removeListener(sp<PackageInfoListener> producer) { - lock_guard<mutex> lock(mMutex); // Lock for updates + lock_guard<mutex> lock(mMutex); // Lock for updates mSubscribers.erase(producer); } UidMapping UidMap::getAndClearOutput() { - lock_guard<mutex> lock(mMutex); // Lock for updates + lock_guard<mutex> lock(mMutex); // Lock for updates - auto ret = UidMapping(mOutput); // Copy that will be returned. + auto ret = UidMapping(mOutput); // Copy that will be returned. mOutput.Clear(); // Re-initialize the initial state for the outputs. This results in extra data being uploaded @@ -154,11 +155,11 @@ void UidMap::printUidMap(FILE* out) { lock_guard<mutex> lock(mMutex); for (auto it : mMap) { - fprintf(out, "%s, v%d (%i)\n", it.second.packageName.c_str(), it.second.versionCode, it.first); + fprintf(out, "%s, v%d (%i)\n", it.second.packageName.c_str(), it.second.versionCode, + it.first); } } - } // namespace statsd } // namespace os } // namespace android diff --git a/cmds/statsd/src/UidMap.h b/cmds/statsd/src/packages/UidMap.h index 1481010a60b8..d550372f2e9f 100644 --- a/cmds/statsd/src/UidMap.h +++ b/cmds/statsd/src/packages/UidMap.h @@ -18,17 +18,17 @@ #define STATSD_UIDMAP_H #include "frameworks/base/cmds/statsd/src/stats_log.pb.h" -#include "PackageInfoListener.h" +#include "packages/PackageInfoListener.h" #include <binder/IResultReceiver.h> #include <binder/IShellCallback.h> #include <log/logprint.h> -#include <mutex> -#include <string> #include <stdio.h> +#include <utils/RefBase.h> +#include <mutex> #include <set> +#include <string> #include <unordered_map> -#include <utils/RefBase.h> using namespace std; @@ -40,12 +40,12 @@ struct AppData { const string packageName; int versionCode; - AppData(const string& a, const int v) : packageName(a), versionCode(v) {}; + AppData(const string& a, const int v) : packageName(a), versionCode(v){}; }; // UidMap keeps track of what the corresponding app name (APK name) and version code for every uid // at any given moment. This map must be updated by StatsCompanionService. -class UidMap : public virtual android::RefBase { +class UidMap : public virtual android::RefBase { public: /* * All three inputs must be the same size, and the jth element in each array refers to the same @@ -93,5 +93,4 @@ private: } // namespace os } // namespace android -#endif //STATSD_UIDMAP_H - +#endif // STATSD_UIDMAP_H diff --git a/cmds/statsd/src/stats_events_copy.proto b/cmds/statsd/src/stats_events_copy.proto new file mode 100644 index 000000000000..5e8ef245119a --- /dev/null +++ b/cmds/statsd/src/stats_events_copy.proto @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// STOPSHIP: this is a duplicate of stats_event.proto with LITE_RUNTIME added +// to produce device side lite proto. We should move statsd to soong so that +// we can generate full and lite library from the same proto file. +syntax = "proto2"; +option optimize_for = LITE_RUNTIME; + +// TODO: Not the right package and class name +package android.os.statsd; +option java_package = "com.android.os"; +option java_outer_classname = "StatsEventProto"; + +/** + * The master event class. This message defines all of the available + * raw stats log events from the Android system, also known as "atoms." + * + * This field contains a single oneof with all of the available messages. + * The stats-log-api-gen tool runs as part of the Android build and + * generates the android.util.StatsLog class, which contains the constants + * and methods that Android uses to log. + * + * This StatsEvent class is not actually built into the Android system. + * Instead, statsd on Android constructs these messages synthetically, + * in the format defined here and in stats_log.proto. + */ +message StatsEvent { + oneof event { + ScreenStateChanged screen_state_changed = 1; + ProcessStateChanged process_state_changed = 2; + WakeLockChanged wakelock_changed = 3; + } +} + +/** + * A WorkSource represents the chained attribution of applications that + * resulted in a particular bit of work being done. + */ +message WorkSource { + // TODO +} + +/* + * ***************************************************************************** + * Below are all of the individual atoms that are logged by Android via statsd + * and Westworld. + * + * RULES: + * - The field ids for each atom must start at 1, and count upwards by 1. + * Skipping field ids is not allowed. + * - These form an API, so renaming, renumbering or removing fields is + * not allowed between android releases. (This is not currently enforced, + * but there will be a tool to enforce this restriction). + * - The types must be built-in protocol buffer types, namely, no sub-messages + * are allowed (yet). The bytes type is also not allowed. + * - The CamelCase name of the message type should match the + * underscore_separated name as defined in StatsEvent. + * - If an atom represents work that can be attributed to an app, there can + * be exactly one WorkSource field. It must be field number 1. + * - A field that is a uid should be a string field, tagged with the [xxx] + * annotation. The generated code on android will be represented by UIDs, + * and those UIDs will be translated in xxx to those strings. + * + * CONVENTIONS: + * - Events are past tense. e.g. ScreenStateChanged, not ScreenStateChange + * - If there is a UID, it goes first. Think in an object-oriented fashion. + * ***************************************************************************** + */ + +/** + * Logs when the screen state changes. + * + * Logged from: + * frameworks/base/services/core/java/com/android/server/am/BatteryStatsService.java + */ +message ScreenStateChanged { + // TODO: Use the real screen state. + enum State { + STATE_UNKNOWN = 0; + STATE_OFF = 1; + STATE_ON = 2; + STATE_DOZE = 3; + STATE_DOZE_SUSPEND = 4; + STATE_VR = 5; + } + // New screen state. + optional State display_state = 1; +} + +/** + * Logs that the state of a process state, as per the activity manager has changed. + * + * Logged from: + * frameworks/base/services/core/java/com/android/server/am/BatteryStatsService.java + */ +message ProcessStateChanged { + // TODO: Use the real (mapped) process states. + optional int32 uid = 1; // TODO: should be a string tagged w/ uid annotation + + // The state. + optional int32 state = 2; +} + +/** + * Logs that the state of a wakelock has changed. + * + * Logged from: + * TODO + */ +message WakeLockChanged { + // TODO: Add attribution instead of uid. + optional int32 uid = 1; + + // The wakelock tag (Called tag in the Java API, sometimes name elsewhere). + optional string tag = 2; + + // TODO: Use a constant instead of boolean? + optional bool state = 3; +} + diff --git a/cmds/statsd/src/stats_log.proto b/cmds/statsd/src/stats_log.proto index 6421b70f1f86..4ca06fa7a2fe 100644 --- a/cmds/statsd/src/stats_log.proto +++ b/cmds/statsd/src/stats_log.proto @@ -22,7 +22,7 @@ package android.os.statsd; option java_package = "com.android.os"; option java_outer_classname = "StatsLog"; -import "frameworks/base/cmds/statsd/src/stats_events.proto"; +import "frameworks/base/cmds/statsd/src/stats_events_copy.proto"; message KeyValuePair { optional int32 key = 1; diff --git a/cmds/statsd/src/stats_util.cpp b/cmds/statsd/src/stats_util.cpp index 978b228891b5..5157adf74607 100644 --- a/cmds/statsd/src/stats_util.cpp +++ b/cmds/statsd/src/stats_util.cpp @@ -128,162 +128,6 @@ EventMetricData parse(log_msg msg) { return eventMetricData; } -StatsdConfig buildFakeConfig() { - // HACK: Hard code a test metric for counting screen on events... - StatsdConfig config; - config.set_config_id(12345L); - - // One count metric to count screen on - CountMetric* metric = config.add_count_metric(); - metric->set_metric_id(20150717L); - metric->set_what("SCREEN_IS_ON"); - metric->mutable_bucket()->set_bucket_size_millis(30 * 1000L); - - // One count metric to count PHOTO_CHANGE_OR_CHROME_CRASH - metric = config.add_count_metric(); - metric->set_metric_id(20150718L); - metric->set_what("PHOTO_PROCESS_STATE_CHANGE"); - metric->mutable_bucket()->set_bucket_size_millis(60 * 1000L); - metric->set_condition("SCREEN_IS_ON"); - - - LogEntryMatcher* eventMatcher = config.add_log_entry_matcher(); - eventMatcher->set_name("SCREEN_IS_ON"); - - SimpleLogEntryMatcher* simpleLogEntryMatcher = eventMatcher->mutable_simple_log_entry_matcher(); - simpleLogEntryMatcher->add_tag(2 /*SCREEN_STATE_CHANGE*/); - simpleLogEntryMatcher->add_key_value_matcher()->mutable_key_matcher()->set_key( - 1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/); - simpleLogEntryMatcher->mutable_key_value_matcher(0)->set_eq_int( - 2 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_ON*/); - - - - eventMatcher = config.add_log_entry_matcher(); - eventMatcher->set_name("SCREEN_IS_OFF"); - - simpleLogEntryMatcher = eventMatcher->mutable_simple_log_entry_matcher(); - simpleLogEntryMatcher->add_tag(2 /*SCREEN_STATE_CHANGE*/); - simpleLogEntryMatcher->add_key_value_matcher()->mutable_key_matcher()->set_key( - 1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/); - simpleLogEntryMatcher->mutable_key_value_matcher(0)->set_eq_int( - 1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_OFF*/); - - - - LogEntryMatcher* procEventMatcher = config.add_log_entry_matcher(); - procEventMatcher->set_name("PHOTO_CRASH"); - - SimpleLogEntryMatcher* simpleLogMatcher2 = procEventMatcher->mutable_simple_log_entry_matcher(); - simpleLogMatcher2->add_tag(1112 /*PROCESS_STATE_CHANGE*/); - KeyValueMatcher* keyValueMatcher = simpleLogMatcher2->add_key_value_matcher(); - keyValueMatcher->mutable_key_matcher()->set_key( - 1002 /*pkg*/); - keyValueMatcher->set_eq_string( - "com.google.android.apps.photos" /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_ON*/); - - keyValueMatcher = simpleLogMatcher2->add_key_value_matcher(); - keyValueMatcher->mutable_key_matcher()->set_key( - 1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/); - keyValueMatcher->set_eq_int(2); - - - procEventMatcher = config.add_log_entry_matcher(); - procEventMatcher->set_name("PHOTO_START"); - - simpleLogMatcher2 = procEventMatcher->mutable_simple_log_entry_matcher(); - simpleLogMatcher2->add_tag(1112 /*PROCESS_STATE_CHANGE*/); - keyValueMatcher = simpleLogMatcher2->add_key_value_matcher(); - keyValueMatcher->mutable_key_matcher()->set_key( - 1002 /*pkg*/); - keyValueMatcher->set_eq_string( - "com.google.android.apps.photos" /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_ON*/); - - keyValueMatcher = simpleLogMatcher2->add_key_value_matcher(); - keyValueMatcher->mutable_key_matcher()->set_key( - 1 /*STATE*/); - keyValueMatcher->set_eq_int(1); - - - procEventMatcher = config.add_log_entry_matcher(); - procEventMatcher->set_name("PHOTO_PROCESS_STATE_CHANGE"); - LogEntryMatcher_Combination* combinationMatcher = procEventMatcher->mutable_combination(); - combinationMatcher->set_operation(LogicalOperation::OR); - combinationMatcher->add_matcher("PHOTO_START"); - combinationMatcher->add_matcher("PHOTO_CRASH"); - - - procEventMatcher = config.add_log_entry_matcher(); - procEventMatcher->set_name("CHROME_CRASH"); - - simpleLogMatcher2 = procEventMatcher->mutable_simple_log_entry_matcher(); - simpleLogMatcher2->add_tag(1112 /*PROCESS_STATE_CHANGE*/); - keyValueMatcher = simpleLogMatcher2->add_key_value_matcher(); - keyValueMatcher->mutable_key_matcher()->set_key( - 1002 /*pkg*/); - keyValueMatcher->set_eq_string( - "com.android.chrome" /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_ON*/); - - keyValueMatcher = simpleLogMatcher2->add_key_value_matcher(); - keyValueMatcher->mutable_key_matcher()->set_key( - 1 /*STATE*/); - keyValueMatcher->set_eq_int(2); - - - - procEventMatcher = config.add_log_entry_matcher(); - procEventMatcher->set_name("PHOTO_CHANGE_OR_CHROME_CRASH"); - combinationMatcher = procEventMatcher->mutable_combination(); - combinationMatcher->set_operation(LogicalOperation::OR); - combinationMatcher->add_matcher("PHOTO_PROCESS_STATE_CHANGE"); - combinationMatcher->add_matcher("CHROME_CRASH"); - - - - Condition* condition = config.add_condition(); - condition->set_name("SCREEN_IS_ON"); - SimpleCondition* simpleCondition = condition->mutable_simple_condition(); - simpleCondition->set_start("SCREEN_IS_ON"); - simpleCondition->set_stop("SCREEN_IS_OFF"); - - - condition = config.add_condition(); - condition->set_name("PHOTO_STARTED"); - - simpleCondition = condition->mutable_simple_condition(); - simpleCondition->set_start("PHOTO_START"); - simpleCondition->set_stop("PHOTO_CRASH"); - - - condition = config.add_condition(); - condition->set_name("SCREEN_IS_OFF"); - - simpleCondition = condition->mutable_simple_condition(); - simpleCondition->set_start("SCREEN_IS_OFF"); - simpleCondition->set_stop("SCREEN_IS_ON"); - - - condition = config.add_condition(); - condition->set_name("SCREEN_IS_EITHER_ON_OFF"); - - Condition_Combination* combination = condition->mutable_combination(); - combination->set_operation(LogicalOperation::OR); - combination->add_condition("SCREEN_IS_ON"); - combination->add_condition("SCREEN_IS_OFF"); - - - condition = config.add_condition(); - condition->set_name("SCREEN_IS_NEITHER_ON_OFF"); - - combination = condition->mutable_combination(); - combination->set_operation(LogicalOperation::NOR); - combination->add_condition("SCREEN_IS_ON"); - combination->add_condition("SCREEN_IS_OFF"); - - return config; -} - - } // namespace statsd } // namespace os } // namespace android diff --git a/cmds/statsd/src/stats_util.h b/cmds/statsd/src/stats_util.h index 25b9bba56280..38174bfae080 100644 --- a/cmds/statsd/src/stats_util.h +++ b/cmds/statsd/src/stats_util.h @@ -16,8 +16,8 @@ #ifndef PARSE_UTIL_H #define PARSE_UTIL_H -#include "DropboxWriter.h" -#include "LogReader.h" +#include "logd/LogReader.h" +#include "storage/DropboxWriter.h" #include <log/logprint.h> #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" @@ -30,8 +30,6 @@ EventMetricData parse(log_msg msg); int getTagId(log_msg msg); -StatsdConfig buildFakeConfig(); - } // namespace statsd } // namespace os } // namespace android diff --git a/cmds/statsd/src/DropboxReader.cpp b/cmds/statsd/src/storage/DropboxReader.cpp index 430e7afc7b9b..c561959f8edd 100644 --- a/cmds/statsd/src/DropboxReader.cpp +++ b/cmds/statsd/src/storage/DropboxReader.cpp @@ -17,14 +17,14 @@ #include <android/os/DropBoxManager.h> #include <androidfw/ZipUtils.h> -#include "DropboxReader.h" +#include "storage/DropboxReader.h" -using android::String16; -using android::ZipUtils; using android::base::unique_fd; using android::binder::Status; using android::os::DropBoxManager; using android::sp; +using android::String16; +using android::ZipUtils; using std::vector; namespace android { diff --git a/cmds/statsd/src/DropboxReader.h b/cmds/statsd/src/storage/DropboxReader.h index a5a28d9113da..a5a28d9113da 100644 --- a/cmds/statsd/src/DropboxReader.h +++ b/cmds/statsd/src/storage/DropboxReader.h diff --git a/cmds/statsd/src/DropboxWriter.cpp b/cmds/statsd/src/storage/DropboxWriter.cpp index b72e530e413a..e59bdbd1eb21 100644 --- a/cmds/statsd/src/DropboxWriter.cpp +++ b/cmds/statsd/src/storage/DropboxWriter.cpp @@ -16,12 +16,12 @@ #include <android/os/DropBoxManager.h> -#include "DropboxWriter.h" +#include "storage/DropboxWriter.h" -using android::String16; using android::binder::Status; using android::os::DropBoxManager; using android::sp; +using android::String16; using std::vector; namespace android { diff --git a/cmds/statsd/src/DropboxWriter.h b/cmds/statsd/src/storage/DropboxWriter.h index d72f1032744a..d72f1032744a 100644 --- a/cmds/statsd/src/DropboxWriter.h +++ b/cmds/statsd/src/storage/DropboxWriter.h diff --git a/cmds/statsd/tests/AnomalyMonitor_test.cpp b/cmds/statsd/tests/AnomalyMonitor_test.cpp index d5b68118d119..59fa16007eef 100644 --- a/cmds/statsd/tests/AnomalyMonitor_test.cpp +++ b/cmds/statsd/tests/AnomalyMonitor_test.cpp @@ -12,9 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -#define LOG_TAG "statsd_test" - -#include "../src/AnomalyMonitor.h" +#include "anomaly/AnomalyMonitor.h" #include <gtest/gtest.h> diff --git a/cmds/statsd/tests/ConditionTracker_test.cpp b/cmds/statsd/tests/ConditionTracker_test.cpp index f8b0fd0796e7..2935ac7136ba 100644 --- a/cmds/statsd/tests/ConditionTracker_test.cpp +++ b/cmds/statsd/tests/ConditionTracker_test.cpp @@ -12,11 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. -#define LOG_TAG "statsd_test" +#include "condition/condition_util.h" +#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" #include <gtest/gtest.h> -#include "../src/condition/condition_util.h" -#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" #include <stdio.h> #include <vector> diff --git a/cmds/statsd/tests/ConfigManager_test.cpp b/cmds/statsd/tests/ConfigManager_test.cpp new file mode 100644 index 000000000000..aa896cafffaf --- /dev/null +++ b/cmds/statsd/tests/ConfigManager_test.cpp @@ -0,0 +1,156 @@ +// Copyright (C) 2017 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "src/config/ConfigManager.h" + +#include <gmock/gmock.h> +#include <gtest/gtest.h> + +#include <stdio.h> +#include <iostream> + +using namespace android; +using namespace android::os::statsd; +using namespace testing; +using namespace std; + +namespace android { +namespace os { +namespace statsd { + +static ostream& operator<<(ostream& os, const StatsdConfig& config) { + return os << "StatsdConfig{id=" << config.config_id() << "}"; +} + +} // namespace statsd +} // namespace os +} // namespace android + +/** + * Mock ConfigListener + */ +class MockListener : public ConfigListener { +public: + MOCK_METHOD2(OnConfigUpdated, void(const ConfigKey& key, const StatsdConfig& config)); + MOCK_METHOD1(OnConfigRemoved, void(const ConfigKey& key)); +}; + +/** + * Validate that the ConfigKey is the one we wanted. + */ +MATCHER_P2(ConfigKeyEq, uid, name, "") { + return arg.GetUid() == uid && arg.GetName() == name; +} + +/** + * Validate that the StatsdConfig is the one we wanted. + */ +MATCHER_P(StatsdConfigEq, configId, "") { + return arg.config_id() == configId; +} + +/** + * Test the addOrUpdate and remove methods + */ +TEST(ConfigManagerTest, TestAddUpdateRemove) { + sp<MockListener> listener = new StrictMock<MockListener>(); + + sp<ConfigManager> manager = new ConfigManager(); + manager->AddListener(listener); + + StatsdConfig config91; + config91.set_config_id(91); + StatsdConfig config92; + config92.set_config_id(92); + StatsdConfig config93; + config93.set_config_id(93); + StatsdConfig config94; + config94.set_config_id(94); + + { + InSequence s; + + // The built-in fake one. + // TODO: Remove this when we get rid of the fake one, and make this + // test loading one from disk somewhere. + EXPECT_CALL(*(listener.get()), + OnConfigUpdated(ConfigKeyEq(0, "fake"), StatsdConfigEq(12345))) + .RetiresOnSaturation(); + manager->Startup(); + + // Add another one + EXPECT_CALL(*(listener.get()), OnConfigUpdated(ConfigKeyEq(1, "zzz"), StatsdConfigEq(91))) + .RetiresOnSaturation(); + manager->UpdateConfig(ConfigKey(1, "zzz"), config91); + + // Update It + EXPECT_CALL(*(listener.get()), OnConfigUpdated(ConfigKeyEq(1, "zzz"), StatsdConfigEq(92))) + .RetiresOnSaturation(); + manager->UpdateConfig(ConfigKey(1, "zzz"), config92); + + // Add one with the same uid but a different name + EXPECT_CALL(*(listener.get()), OnConfigUpdated(ConfigKeyEq(1, "yyy"), StatsdConfigEq(93))) + .RetiresOnSaturation(); + manager->UpdateConfig(ConfigKey(1, "yyy"), config93); + + // Add one with the same name but a different uid + EXPECT_CALL(*(listener.get()), OnConfigUpdated(ConfigKeyEq(2, "zzz"), StatsdConfigEq(94))) + .RetiresOnSaturation(); + manager->UpdateConfig(ConfigKey(2, "zzz"), config94); + + // Remove (1,yyy) + EXPECT_CALL(*(listener.get()), OnConfigRemoved(ConfigKeyEq(1, "yyy"))) + .RetiresOnSaturation(); + manager->RemoveConfig(ConfigKey(1, "yyy")); + + // Remove (2,zzz) + EXPECT_CALL(*(listener.get()), OnConfigRemoved(ConfigKeyEq(2, "zzz"))) + .RetiresOnSaturation(); + manager->RemoveConfig(ConfigKey(2, "zzz")); + + // Remove (1,zzz) + EXPECT_CALL(*(listener.get()), OnConfigRemoved(ConfigKeyEq(1, "zzz"))) + .RetiresOnSaturation(); + manager->RemoveConfig(ConfigKey(1, "zzz")); + + // Remove (2,zzz) again and we shouldn't get the callback + manager->RemoveConfig(ConfigKey(2, "zzz")); + } +} + +/** + * Test removing all of the configs for a uid. + */ +TEST(ConfigManagerTest, TestRemoveUid) { + sp<MockListener> listener = new StrictMock<MockListener>(); + + sp<ConfigManager> manager = new ConfigManager(); + manager->AddListener(listener); + + StatsdConfig config; + + EXPECT_CALL(*(listener.get()), OnConfigUpdated(_, _)).Times(6); + EXPECT_CALL(*(listener.get()), OnConfigRemoved(ConfigKeyEq(2, "xxx"))); + EXPECT_CALL(*(listener.get()), OnConfigRemoved(ConfigKeyEq(2, "yyy"))); + EXPECT_CALL(*(listener.get()), OnConfigRemoved(ConfigKeyEq(2, "zzz"))); + + manager->Startup(); + manager->UpdateConfig(ConfigKey(1, "aaa"), config); + manager->UpdateConfig(ConfigKey(2, "xxx"), config); + manager->UpdateConfig(ConfigKey(2, "yyy"), config); + manager->UpdateConfig(ConfigKey(2, "zzz"), config); + manager->UpdateConfig(ConfigKey(3, "bbb"), config); + + manager->RemoveConfigs(2); +} diff --git a/cmds/statsd/tests/LogEntryMatcher_test.cpp b/cmds/statsd/tests/LogEntryMatcher_test.cpp index 606980178e2a..3bc98624ff75 100644 --- a/cmds/statsd/tests/LogEntryMatcher_test.cpp +++ b/cmds/statsd/tests/LogEntryMatcher_test.cpp @@ -12,15 +12,14 @@ // See the License for the specific language governing permissions and // limitations under the License. -#define LOG_TAG "statsd_test" +#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" +#include "matchers/matcher_util.h" +#include "stats_util.h" #include <gtest/gtest.h> #include <log/log_event_list.h> #include <log/log_read.h> #include <log/logprint.h> -#include "../src/matchers/matcher_util.h" -#include "../src/stats_util.h" -#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" #include <stdio.h> diff --git a/cmds/statsd/tests/LogReader_test.cpp b/cmds/statsd/tests/LogReader_test.cpp index 2002143edfc1..7ce1d6a71c85 100644 --- a/cmds/statsd/tests/LogReader_test.cpp +++ b/cmds/statsd/tests/LogReader_test.cpp @@ -12,8 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -#define LOG_TAG "statsd_test" - #include <gtest/gtest.h> #include <stdio.h> diff --git a/cmds/statsd/tests/MetricsManager_test.cpp b/cmds/statsd/tests/MetricsManager_test.cpp index 673c15686f71..32661dcc4752 100644 --- a/cmds/statsd/tests/MetricsManager_test.cpp +++ b/cmds/statsd/tests/MetricsManager_test.cpp @@ -12,14 +12,13 @@ // See the License for the specific language governing permissions and // limitations under the License. -#define LOG_TAG "statsd_test" - #include <gtest/gtest.h> -#include "../src/condition/ConditionTracker.h" -#include "../src/matchers/LogMatchingTracker.h" -#include "../src/metrics/CountMetricProducer.h" -#include "../src/metrics/MetricProducer.h" -#include "../src/metrics/metrics_manager_util.h" + +#include "src/condition/ConditionTracker.h" +#include "src/matchers/LogMatchingTracker.h" +#include "src/metrics/CountMetricProducer.h" +#include "src/metrics/MetricProducer.h" +#include "src/metrics/metrics_manager_util.h" #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" diff --git a/cmds/statsd/tests/UidMap_test.cpp b/cmds/statsd/tests/UidMap_test.cpp index b6f14493cb36..f9a90e4ee29b 100644 --- a/cmds/statsd/tests/UidMap_test.cpp +++ b/cmds/statsd/tests/UidMap_test.cpp @@ -12,10 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. -#define LOG_TAG "statsd_test" +#include "packages/UidMap.h" #include <gtest/gtest.h> -#include "../src/UidMap.h" + #include <stdio.h> using namespace android; @@ -66,4 +66,4 @@ TEST(UidMapTest, TestAddAndRemove) { } #else GTEST_LOG_(INFO) << "This test does nothing.\n"; -#endif
\ No newline at end of file +#endif diff --git a/cmds/statsd/tests/indexed_priority_queue_test.cpp b/cmds/statsd/tests/indexed_priority_queue_test.cpp index 74a482eace58..600b95356083 100644 --- a/cmds/statsd/tests/indexed_priority_queue_test.cpp +++ b/cmds/statsd/tests/indexed_priority_queue_test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "../src/indexed_priority_queue.h" +#include "src/anomaly/indexed_priority_queue.h" #include <gtest/gtest.h> diff --git a/core/java/android/accessibilityservice/GestureDescription.java b/core/java/android/accessibilityservice/GestureDescription.java index 92567d758856..56f4ae2b5832 100644 --- a/core/java/android/accessibilityservice/GestureDescription.java +++ b/core/java/android/accessibilityservice/GestureDescription.java @@ -428,6 +428,18 @@ public final class GestureDescription { } @Override + public String toString() { + return "TouchPoint{" + + "mStrokeId=" + mStrokeId + + ", mContinuedStrokeId=" + mContinuedStrokeId + + ", mIsStartOfPath=" + mIsStartOfPath + + ", mIsEndOfPath=" + mIsEndOfPath + + ", mX=" + mX + + ", mY=" + mY + + '}'; + } + + @Override public int describeContents() { return 0; } diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 252959a04d4f..85f73bb7c0ef 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -6259,6 +6259,8 @@ public class Activity extends ContextThemeWrapper final AutofillManager afm = getAutofillManager(); if (afm != null) { afm.dump(prefix, writer); + } else { + writer.print(prefix); writer.println("No AutofillManager"); } } diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index 027e811a3273..fc4c8d7f0666 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -17,6 +17,7 @@ package android.app; import android.Manifest; +import android.annotation.DrawableRes; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; @@ -941,11 +942,14 @@ public class ActivityManager { ATTR_TASKDESCRIPTION_PREFIX + "color"; private static final String ATTR_TASKDESCRIPTIONCOLOR_BACKGROUND = ATTR_TASKDESCRIPTION_PREFIX + "colorBackground"; - private static final String ATTR_TASKDESCRIPTIONICONFILENAME = + private static final String ATTR_TASKDESCRIPTIONICON_FILENAME = ATTR_TASKDESCRIPTION_PREFIX + "icon_filename"; + private static final String ATTR_TASKDESCRIPTIONICON_RESOURCE = + ATTR_TASKDESCRIPTION_PREFIX + "icon_resource"; private String mLabel; private Bitmap mIcon; + private int mIconRes; private String mIconFilename; private int mColorPrimary; private int mColorBackground; @@ -959,9 +963,27 @@ public class ActivityManager { * @param icon An icon that represents the current state of this task. * @param colorPrimary A color to override the theme's primary color. This color must be * opaque. + * @deprecated use TaskDescription constructor with icon resource instead */ + @Deprecated public TaskDescription(String label, Bitmap icon, int colorPrimary) { - this(label, icon, null, colorPrimary, 0, 0, 0); + this(label, icon, 0, null, colorPrimary, 0, 0, 0); + if ((colorPrimary != 0) && (Color.alpha(colorPrimary) != 255)) { + throw new RuntimeException("A TaskDescription's primary color should be opaque"); + } + } + + /** + * Creates the TaskDescription to the specified values. + * + * @param label A label and description of the current state of this task. + * @param iconRes A drawable resource of an icon that represents the current state of this + * activity. + * @param colorPrimary A color to override the theme's primary color. This color must be + * opaque. + */ + public TaskDescription(String label, @DrawableRes int iconRes, int colorPrimary) { + this(label, null, iconRes, null, colorPrimary, 0, 0, 0); if ((colorPrimary != 0) && (Color.alpha(colorPrimary) != 255)) { throw new RuntimeException("A TaskDescription's primary color should be opaque"); } @@ -972,9 +994,22 @@ public class ActivityManager { * * @param label A label and description of the current state of this activity. * @param icon An icon that represents the current state of this activity. + * @deprecated use TaskDescription constructor with icon resource instead */ + @Deprecated public TaskDescription(String label, Bitmap icon) { - this(label, icon, null, 0, 0, 0, 0); + this(label, icon, 0, null, 0, 0, 0, 0); + } + + /** + * Creates the TaskDescription to the specified values. + * + * @param label A label and description of the current state of this activity. + * @param iconRes A drawable resource of an icon that represents the current state of this + * activity. + */ + public TaskDescription(String label, @DrawableRes int iconRes) { + this(label, null, iconRes, null, 0, 0, 0, 0); } /** @@ -983,21 +1018,22 @@ public class ActivityManager { * @param label A label and description of the current state of this activity. */ public TaskDescription(String label) { - this(label, null, null, 0, 0, 0, 0); + this(label, null, 0, null, 0, 0, 0, 0); } /** * Creates an empty TaskDescription. */ public TaskDescription() { - this(null, null, null, 0, 0, 0, 0); + this(null, null, 0, null, 0, 0, 0, 0); } /** @hide */ - public TaskDescription(String label, Bitmap icon, String iconFilename, int colorPrimary, - int colorBackground, int statusBarColor, int navigationBarColor) { + public TaskDescription(String label, Bitmap bitmap, int iconRes, String iconFilename, + int colorPrimary, int colorBackground, int statusBarColor, int navigationBarColor) { mLabel = label; - mIcon = icon; + mIcon = bitmap; + mIconRes = iconRes; mIconFilename = iconFilename; mColorPrimary = colorPrimary; mColorBackground = colorBackground; @@ -1019,6 +1055,7 @@ public class ActivityManager { public void copyFrom(TaskDescription other) { mLabel = other.mLabel; mIcon = other.mIcon; + mIconRes = other.mIconRes; mIconFilename = other.mIconFilename; mColorPrimary = other.mColorPrimary; mColorBackground = other.mColorBackground; @@ -1034,6 +1071,7 @@ public class ActivityManager { public void copyFromPreserveHiddenFields(TaskDescription other) { mLabel = other.mLabel; mIcon = other.mIcon; + mIconRes = other.mIconRes; mIconFilename = other.mIconFilename; mColorPrimary = other.mColorPrimary; if (other.mColorBackground != 0) { @@ -1106,6 +1144,14 @@ public class ActivityManager { } /** + * Sets the icon resource for this task description. + * @hide + */ + public void setIcon(int iconRes) { + mIconRes = iconRes; + } + + /** * Moves the icon bitmap reference from an actual Bitmap to a file containing the * bitmap. * @hide @@ -1133,6 +1179,13 @@ public class ActivityManager { } /** @hide */ + @TestApi + public int getIconResource() { + return mIconRes; + } + + /** @hide */ + @TestApi public String getIconFilename() { return mIconFilename; } @@ -1198,7 +1251,10 @@ public class ActivityManager { Integer.toHexString(mColorBackground)); } if (mIconFilename != null) { - out.attribute(null, ATTR_TASKDESCRIPTIONICONFILENAME, mIconFilename); + out.attribute(null, ATTR_TASKDESCRIPTIONICON_FILENAME, mIconFilename); + } + if (mIconRes != 0) { + out.attribute(null, ATTR_TASKDESCRIPTIONICON_RESOURCE, Integer.toString(mIconRes)); } } @@ -1210,8 +1266,10 @@ public class ActivityManager { setPrimaryColor((int) Long.parseLong(attrValue, 16)); } else if (ATTR_TASKDESCRIPTIONCOLOR_BACKGROUND.equals(attrName)) { setBackgroundColor((int) Long.parseLong(attrValue, 16)); - } else if (ATTR_TASKDESCRIPTIONICONFILENAME.equals(attrName)) { + } else if (ATTR_TASKDESCRIPTIONICON_FILENAME.equals(attrName)) { setIconFilename(attrValue); + } else if (ATTR_TASKDESCRIPTIONICON_RESOURCE.equals(attrName)) { + setIcon(Integer.parseInt(attrValue, 10)); } } @@ -1234,6 +1292,7 @@ public class ActivityManager { dest.writeInt(1); mIcon.writeToParcel(dest, 0); } + dest.writeInt(mIconRes); dest.writeInt(mColorPrimary); dest.writeInt(mColorBackground); dest.writeInt(mStatusBarColor); @@ -1249,6 +1308,7 @@ public class ActivityManager { public void readFromParcel(Parcel source) { mLabel = source.readInt() > 0 ? source.readString() : null; mIcon = source.readInt() > 0 ? Bitmap.CREATOR.createFromParcel(source) : null; + mIconRes = source.readInt(); mColorPrimary = source.readInt(); mColorBackground = source.readInt(); mStatusBarColor = source.readInt(); @@ -1269,8 +1329,8 @@ public class ActivityManager { @Override public String toString() { return "TaskDescription Label: " + mLabel + " Icon: " + mIcon + - " IconFilename: " + mIconFilename + " colorPrimary: " + mColorPrimary + - " colorBackground: " + mColorBackground + + " IconRes: " + mIconRes + " IconFilename: " + mIconFilename + + " colorPrimary: " + mColorPrimary + " colorBackground: " + mColorBackground + " statusBarColor: " + mColorBackground + " navigationBarColor: " + mNavigationBarColor; } diff --git a/core/java/android/slice/Slice.java b/core/java/android/app/slice/Slice.java index 576865480e04..7f9f74b4819a 100644 --- a/core/java/android/slice/Slice.java +++ b/core/java/android/app/slice/Slice.java @@ -14,38 +14,35 @@ * limitations under the License. */ -package android.slice; - -import static android.slice.SliceItem.TYPE_ACTION; -import static android.slice.SliceItem.TYPE_COLOR; -import static android.slice.SliceItem.TYPE_IMAGE; -import static android.slice.SliceItem.TYPE_REMOTE_INPUT; -import static android.slice.SliceItem.TYPE_REMOTE_VIEW; -import static android.slice.SliceItem.TYPE_SLICE; -import static android.slice.SliceItem.TYPE_TEXT; -import static android.slice.SliceItem.TYPE_TIMESTAMP; +package android.app.slice; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.StringDef; import android.app.PendingIntent; import android.app.RemoteInput; +import android.content.ContentResolver; +import android.content.IContentProvider; import android.graphics.drawable.Icon; import android.net.Uri; +import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; +import android.os.RemoteException; import android.widget.RemoteViews; import com.android.internal.util.ArrayUtils; +import com.android.internal.util.Preconditions; import java.util.ArrayList; import java.util.Arrays; +import java.util.List; /** * A slice is a piece of app content and actions that can be surfaced outside of the app. * * <p>They are constructed using {@link Builder} in a tree structure * that provides the OS some information about how the content should be displayed. - * @hide */ public final class Slice implements Parcelable { @@ -53,7 +50,7 @@ public final class Slice implements Parcelable { * @hide */ @StringDef({HINT_TITLE, HINT_LIST, HINT_LIST_ITEM, HINT_LARGE, HINT_ACTIONS, HINT_SELECTED, - HINT_SOURCE, HINT_MESSAGE, HINT_HORIZONTAL, HINT_NO_TINT}) + HINT_SOURCE, HINT_MESSAGE, HINT_HORIZONTAL, HINT_NO_TINT, HINT_PARTIAL}) public @interface SliceHint{ } /** @@ -102,6 +99,12 @@ public final class Slice implements Parcelable { * Hint to indicate that this content should not be tinted. */ public static final String HINT_NO_TINT = "no_tint"; + /** + * Hint to indicate that this slice is incomplete and an update will be sent once + * loading is complete. Slices which contain HINT_PARTIAL will not be cached by the + * OS and should not be cached by apps. + */ + public static final String HINT_PARTIAL = "partial"; // These two are coming over from prototyping, but we probably don't want in // public API, at least not right now. @@ -109,19 +112,12 @@ public final class Slice implements Parcelable { * @hide */ public static final String HINT_ALT = "alt"; - /** - * @hide - */ - public static final String HINT_PARTIAL = "partial"; private final SliceItem[] mItems; private final @SliceHint String[] mHints; private Uri mUri; - /** - * @hide - */ - public Slice(ArrayList<SliceItem> items, @SliceHint String[] hints, Uri uri) { + Slice(ArrayList<SliceItem> items, @SliceHint String[] hints, Uri uri) { mHints = hints; mItems = items.toArray(new SliceItem[items.size()]); mUri = uri; @@ -147,15 +143,15 @@ public final class Slice implements Parcelable { /** * @return All child {@link SliceItem}s that this Slice contains. */ - public SliceItem[] getItems() { - return mItems; + public List<SliceItem> getItems() { + return Arrays.asList(mItems); } /** * @return All hints associated with this Slice. */ - public @SliceHint String[] getHints() { - return mHints; + public @SliceHint List<String> getHints() { + return Arrays.asList(mHints); } /** @@ -163,14 +159,14 @@ public final class Slice implements Parcelable { */ public SliceItem getPrimaryIcon() { for (SliceItem item : getItems()) { - if (item.getType() == TYPE_IMAGE) { + if (item.getType() == SliceItem.TYPE_IMAGE) { return item; } - if (!(item.getType() == TYPE_SLICE && item.hasHint(Slice.HINT_LIST)) + if (!(item.getType() == SliceItem.TYPE_SLICE && item.hasHint(Slice.HINT_LIST)) && !item.hasHint(Slice.HINT_ACTIONS) && !item.hasHint(Slice.HINT_LIST_ITEM) - && (item.getType() != TYPE_ACTION)) { - SliceItem icon = SliceQuery.find(item, TYPE_IMAGE); + && (item.getType() != SliceItem.TYPE_ACTION)) { + SliceItem icon = SliceQuery.find(item, SliceItem.TYPE_IMAGE); if (icon != null) return icon; } } @@ -235,10 +231,18 @@ public final class Slice implements Parcelable { } /** + * Add hints to the Slice being constructed + */ + public Builder addHints(@SliceHint List<String> hints) { + return addHints(hints.toArray(new String[hints.size()])); + } + + /** * Add a sub-slice to the slice being constructed */ public Builder addSubSlice(@NonNull Slice slice) { - mItems.add(new SliceItem(slice, TYPE_SLICE, slice.getHints())); + mItems.add(new SliceItem(slice, SliceItem.TYPE_SLICE, slice.getHints().toArray( + new String[slice.getHints().size()]))); return this; } @@ -246,7 +250,7 @@ public final class Slice implements Parcelable { * Add an action to the slice being constructed */ public Slice.Builder addAction(@NonNull PendingIntent action, @NonNull Slice s) { - mItems.add(new SliceItem(action, s, TYPE_ACTION, new String[0])); + mItems.add(new SliceItem(action, s, SliceItem.TYPE_ACTION, new String[0])); return this; } @@ -254,31 +258,53 @@ public final class Slice implements Parcelable { * Add text to the slice being constructed */ public Builder addText(CharSequence text, @SliceHint String... hints) { - mItems.add(new SliceItem(text, TYPE_TEXT, hints)); + mItems.add(new SliceItem(text, SliceItem.TYPE_TEXT, hints)); return this; } /** + * Add text to the slice being constructed + */ + public Builder addText(CharSequence text, @SliceHint List<String> hints) { + return addText(text, hints.toArray(new String[hints.size()])); + } + + /** * Add an image to the slice being constructed */ public Builder addIcon(Icon icon, @SliceHint String... hints) { - mItems.add(new SliceItem(icon, TYPE_IMAGE, hints)); + mItems.add(new SliceItem(icon, SliceItem.TYPE_IMAGE, hints)); return this; } /** + * Add an image to the slice being constructed + */ + public Builder addIcon(Icon icon, @SliceHint List<String> hints) { + return addIcon(icon, hints.toArray(new String[hints.size()])); + } + + /** * @hide This isn't final */ public Builder addRemoteView(RemoteViews remoteView, @SliceHint String... hints) { - mItems.add(new SliceItem(remoteView, TYPE_REMOTE_VIEW, hints)); + mItems.add(new SliceItem(remoteView, SliceItem.TYPE_REMOTE_VIEW, hints)); return this; } /** * Add remote input to the slice being constructed */ + public Slice.Builder addRemoteInput(RemoteInput remoteInput, + @SliceHint List<String> hints) { + return addRemoteInput(remoteInput, hints.toArray(new String[hints.size()])); + } + + /** + * Add remote input to the slice being constructed + */ public Slice.Builder addRemoteInput(RemoteInput remoteInput, @SliceHint String... hints) { - mItems.add(new SliceItem(remoteInput, TYPE_REMOTE_INPUT, hints)); + mItems.add(new SliceItem(remoteInput, SliceItem.TYPE_REMOTE_INPUT, hints)); return this; } @@ -286,19 +312,33 @@ public final class Slice implements Parcelable { * Add a color to the slice being constructed */ public Builder addColor(int color, @SliceHint String... hints) { - mItems.add(new SliceItem(color, TYPE_COLOR, hints)); + mItems.add(new SliceItem(color, SliceItem.TYPE_COLOR, hints)); return this; } /** + * Add a color to the slice being constructed + */ + public Builder addColor(int color, @SliceHint List<String> hints) { + return addColor(color, hints.toArray(new String[hints.size()])); + } + + /** * Add a timestamp to the slice being constructed */ public Slice.Builder addTimestamp(long time, @SliceHint String... hints) { - mItems.add(new SliceItem(time, TYPE_TIMESTAMP, hints)); + mItems.add(new SliceItem(time, SliceItem.TYPE_TIMESTAMP, hints)); return this; } /** + * Add a timestamp to the slice being constructed + */ + public Slice.Builder addTimestamp(long time, @SliceHint List<String> hints) { + return addTimestamp(time, hints.toArray(new String[hints.size()])); + } + + /** * Construct the slice. */ public Slice build() { @@ -322,18 +362,18 @@ public final class Slice implements Parcelable { * @hide * @return A string representation of this slice. */ - public String getString() { - return getString(""); + public String toString() { + return toString(""); } - private String getString(String indent) { + private String toString(String indent) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < mItems.length; i++) { sb.append(indent); - if (mItems[i].getType() == TYPE_SLICE) { + if (mItems[i].getType() == SliceItem.TYPE_SLICE) { sb.append("slice:\n"); - sb.append(mItems[i].getSlice().getString(indent + " ")); - } else if (mItems[i].getType() == TYPE_TEXT) { + sb.append(mItems[i].getSlice().toString(indent + " ")); + } else if (mItems[i].getType() == SliceItem.TYPE_TEXT) { sb.append("text: "); sb.append(mItems[i].getText()); sb.append("\n"); @@ -344,4 +384,34 @@ public final class Slice implements Parcelable { } return sb.toString(); } + + /** + * Turns a slice Uri into slice content. + * + * @param resolver ContentResolver to be used. + * @param uri The URI to a slice provider + * @return The Slice provided by the app or null if none is given. + * @see Slice + */ + public static @Nullable Slice bindSlice(ContentResolver resolver, @NonNull Uri uri) { + Preconditions.checkNotNull(uri, "uri"); + IContentProvider provider = resolver.acquireProvider(uri); + if (provider == null) { + throw new IllegalArgumentException("Unknown URI " + uri); + } + try { + Bundle extras = new Bundle(); + extras.putParcelable(SliceProvider.EXTRA_BIND_URI, uri); + final Bundle res = provider.call(resolver.getPackageName(), SliceProvider.METHOD_SLICE, + null, extras); + Bundle.setDefusable(res, true); + return res.getParcelable(SliceProvider.EXTRA_SLICE); + } catch (RemoteException e) { + // Arbitrary and not worth documenting, as Activity + // Manager will kill this process shortly anyway. + return null; + } finally { + resolver.releaseProvider(provider); + } + } } diff --git a/core/java/android/slice/SliceItem.java b/core/java/android/app/slice/SliceItem.java index 2827ab9d994c..6e69b0511207 100644 --- a/core/java/android/slice/SliceItem.java +++ b/core/java/android/app/slice/SliceItem.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package android.slice; +package android.app.slice; import android.annotation.IntDef; import android.annotation.NonNull; @@ -23,13 +23,15 @@ import android.app.RemoteInput; import android.graphics.drawable.Icon; import android.os.Parcel; import android.os.Parcelable; -import android.slice.Slice.SliceHint; import android.text.TextUtils; import android.util.Pair; import android.widget.RemoteViews; import com.android.internal.util.ArrayUtils; +import java.util.Arrays; +import java.util.List; + /** * A SliceItem is a single unit in the tree structure of a {@link Slice}. @@ -47,7 +49,6 @@ import com.android.internal.util.ArrayUtils; * The hints that a {@link SliceItem} are a set of strings which annotate * the content. The hints that are guaranteed to be understood by the system * are defined on {@link Slice}. - * @hide */ public final class SliceItem implements Parcelable { @@ -97,14 +98,15 @@ public final class SliceItem implements Parcelable { /** * @hide */ - protected @SliceHint String[] mHints; + protected @Slice.SliceHint + String[] mHints; private final int mType; private final Object mObj; /** * @hide */ - public SliceItem(Object obj, @SliceType int type, @SliceHint String[] hints) { + public SliceItem(Object obj, @SliceType int type, @Slice.SliceHint String[] hints) { mHints = hints; mType = type; mObj = obj; @@ -113,7 +115,7 @@ public final class SliceItem implements Parcelable { /** * @hide */ - public SliceItem(PendingIntent intent, Slice slice, int type, @SliceHint String[] hints) { + public SliceItem(PendingIntent intent, Slice slice, int type, @Slice.SliceHint String[] hints) { this(new Pair<>(intent, slice), type, hints); } @@ -121,14 +123,14 @@ public final class SliceItem implements Parcelable { * Gets all hints associated with this SliceItem. * @return Array of hints. */ - public @NonNull @SliceHint String[] getHints() { - return mHints; + public @NonNull @Slice.SliceHint List<String> getHints() { + return Arrays.asList(mHints); } /** * @hide */ - public void addHint(@SliceHint String hint) { + public void addHint(@Slice.SliceHint String hint) { mHints = ArrayUtils.appendElement(String.class, mHints, hint); } @@ -206,7 +208,7 @@ public final class SliceItem implements Parcelable { * @param hint The hint to check for * @return true if this item contains the given hint */ - public boolean hasHint(@SliceHint String hint) { + public boolean hasHint(@Slice.SliceHint String hint) { return ArrayUtils.contains(mHints, hint); } @@ -234,7 +236,7 @@ public final class SliceItem implements Parcelable { /** * @hide */ - public boolean hasHints(@SliceHint String[] hints) { + public boolean hasHints(@Slice.SliceHint String[] hints) { if (hints == null) return true; for (String hint : hints) { if (!TextUtils.isEmpty(hint) && !ArrayUtils.contains(mHints, hint)) { @@ -247,7 +249,7 @@ public final class SliceItem implements Parcelable { /** * @hide */ - public boolean hasAnyHints(@SliceHint String[] hints) { + public boolean hasAnyHints(@Slice.SliceHint String[] hints) { if (hints == null) return false; for (String hint : hints) { if (ArrayUtils.contains(mHints, hint)) { diff --git a/core/java/android/slice/SliceProvider.java b/core/java/android/app/slice/SliceProvider.java index 4e21371b26b5..df87b4556ee5 100644 --- a/core/java/android/slice/SliceProvider.java +++ b/core/java/android/app/slice/SliceProvider.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package android.slice; +package android.app.slice; import android.Manifest.permission; import android.content.ContentProvider; @@ -26,6 +26,8 @@ import android.os.Bundle; import android.os.CancellationSignal; import android.os.Handler; import android.os.Looper; +import android.os.StrictMode; +import android.os.StrictMode.ThreadPolicy; import android.util.Log; import java.util.concurrent.CountDownLatch; @@ -51,10 +53,15 @@ import java.util.concurrent.CountDownLatch; * </pre> * * @see Slice - * @hide */ public abstract class SliceProvider extends ContentProvider { + /** + * This is the Android platform's MIME type for a slice: URI + * containing a slice implemented through {@link SliceProvider}. + */ + public static final String SLICE_TYPE = "vnd.android.slice"; + private static final String TAG = "SliceProvider"; /** * @hide @@ -73,8 +80,18 @@ public abstract class SliceProvider extends ContentProvider { /** * Implemented to create a slice. Will be called on the main thread. + * <p> + * onBindSlice should return as quickly as possible so that the UI tied + * to this slice can be responsive. No network or other IO will be allowed + * during onBindSlice. Any loading that needs to be done should happen + * off the main thread with a call to {@link ContentResolver#notifyChange(Uri, ContentObserver)} + * when the app is ready to provide the complete data in onBindSlice. + * <p> + * * @see {@link Slice}. + * @see {@link Slice#HINT_PARTIAL} */ + // TODO: Provide alternate notifyChange that takes in the slice (i.e. notifyChange(Uri, Slice)). public abstract Slice onBindSlice(Uri sliceUri); @Override @@ -120,11 +137,11 @@ public abstract class SliceProvider extends ContentProvider { @Override public final String getType(Uri uri) { if (DEBUG) Log.d(TAG, "getType " + uri); - return null; + return SLICE_TYPE; } @Override - public final Bundle call(String method, String arg, Bundle extras) { + public Bundle call(String method, String arg, Bundle extras) { if (method.equals(METHOD_SLICE)) { getContext().enforceCallingPermission(permission.BIND_SLICE, "Slice binding requires the permission BIND_SLICE"); @@ -143,8 +160,17 @@ public abstract class SliceProvider extends ContentProvider { CountDownLatch latch = new CountDownLatch(1); Handler mainHandler = new Handler(Looper.getMainLooper()); mainHandler.post(() -> { - output[0] = onBindSlice(sliceUri); - latch.countDown(); + ThreadPolicy oldPolicy = StrictMode.getThreadPolicy(); + try { + StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder() + .detectAll() + .penaltyDeath() + .build()); + output[0] = onBindSlice(sliceUri); + } finally { + StrictMode.setThreadPolicy(oldPolicy); + latch.countDown(); + } }); try { latch.await(); diff --git a/core/java/android/slice/SliceQuery.java b/core/java/android/app/slice/SliceQuery.java index d99b26a507e4..d1fe2c90f7a4 100644 --- a/core/java/android/slice/SliceQuery.java +++ b/core/java/android/app/slice/SliceQuery.java @@ -14,12 +14,8 @@ * limitations under the License. */ -package android.slice; +package android.app.slice; -import static android.slice.SliceItem.TYPE_ACTION; -import static android.slice.SliceItem.TYPE_SLICE; - -import java.util.Arrays; import java.util.Iterator; import java.util.LinkedList; import java.util.List; @@ -114,7 +110,9 @@ public class SliceQuery { * @hide */ public static SliceItem find(Slice s, int type, String[] hints, String[] nonHints) { - return find(new SliceItem(s, TYPE_SLICE, s.getHints()), type, hints, nonHints); + List<String> h = s.getHints(); + return find(new SliceItem(s, SliceItem.TYPE_SLICE, h.toArray(new String[h.size()])), type, + hints, nonHints); } /** @@ -140,8 +138,9 @@ public class SliceQuery { @Override public SliceItem next() { SliceItem item = items.poll(); - if (item.getType() == TYPE_SLICE || item.getType() == TYPE_ACTION) { - items.addAll(Arrays.asList(item.getSlice().getItems())); + if (item.getType() == SliceItem.TYPE_SLICE + || item.getType() == SliceItem.TYPE_ACTION) { + items.addAll(item.getSlice().getItems()); } return item; } diff --git a/core/java/android/slice/views/ActionRow.java b/core/java/android/app/slice/views/ActionRow.java index 93e9c0352580..c7d99f7fd443 100644 --- a/core/java/android/slice/views/ActionRow.java +++ b/core/java/android/app/slice/views/ActionRow.java @@ -14,19 +14,19 @@ * limitations under the License. */ -package android.slice.views; +package android.app.slice.views; import android.app.PendingIntent; import android.app.PendingIntent.CanceledException; import android.app.RemoteInput; +import android.app.slice.Slice; +import android.app.slice.SliceItem; +import android.app.slice.SliceQuery; import android.content.Context; import android.content.res.ColorStateList; import android.graphics.Color; import android.graphics.drawable.Icon; import android.os.AsyncTask; -import android.slice.Slice; -import android.slice.SliceItem; -import android.slice.SliceQuery; import android.util.TypedValue; import android.view.View; import android.view.ViewParent; diff --git a/core/java/android/slice/views/GridView.java b/core/java/android/app/slice/views/GridView.java index 18a90f7d1405..6f30c507b052 100644 --- a/core/java/android/slice/views/GridView.java +++ b/core/java/android/app/slice/views/GridView.java @@ -14,16 +14,16 @@ * limitations under the License. */ -package android.slice.views; +package android.app.slice.views; import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; +import android.app.slice.Slice; +import android.app.slice.SliceItem; +import android.app.slice.views.LargeSliceAdapter.SliceListView; import android.content.Context; import android.graphics.Color; -import android.slice.Slice; -import android.slice.SliceItem; -import android.slice.views.LargeSliceAdapter.SliceListView; import android.util.AttributeSet; import android.util.TypedValue; import android.view.Gravity; @@ -38,7 +38,7 @@ import android.widget.TextView; import com.android.internal.R; import java.util.ArrayList; -import java.util.Arrays; +import java.util.List; /** * @hide @@ -76,10 +76,10 @@ public class GridView extends LinearLayout implements SliceListView { removeAllViews(); int total = 1; if (slice.getType() == SliceItem.TYPE_SLICE) { - SliceItem[] items = slice.getSlice().getItems(); - total = items.length; + List<SliceItem> items = slice.getSlice().getItems(); + total = items.size(); for (int i = 0; i < total; i++) { - SliceItem item = items[i]; + SliceItem item = items.get(i); if (isFull()) { continue; } @@ -142,7 +142,7 @@ public class GridView extends LinearLayout implements SliceListView { // TODO: Unify sporadic inflates that happen throughout the code. ArrayList<SliceItem> items = new ArrayList<>(); if (item.getType() == SliceItem.TYPE_SLICE) { - items.addAll(Arrays.asList(item.getSlice().getItems())); + items.addAll(item.getSlice().getItems()); } items.forEach(i -> { Context context = getContext(); diff --git a/core/java/android/slice/views/LargeSliceAdapter.java b/core/java/android/app/slice/views/LargeSliceAdapter.java index e77a1b2af745..6794ff984152 100644 --- a/core/java/android/slice/views/LargeSliceAdapter.java +++ b/core/java/android/app/slice/views/LargeSliceAdapter.java @@ -14,13 +14,13 @@ * limitations under the License. */ -package android.slice.views; +package android.app.slice.views; +import android.app.slice.Slice; +import android.app.slice.SliceItem; +import android.app.slice.SliceQuery; +import android.app.slice.views.LargeSliceAdapter.SliceViewHolder; import android.content.Context; -import android.slice.Slice; -import android.slice.SliceItem; -import android.slice.SliceQuery; -import android.slice.views.LargeSliceAdapter.SliceViewHolder; import android.util.ArrayMap; import android.view.LayoutInflater; import android.view.View; diff --git a/core/java/android/slice/views/LargeTemplateView.java b/core/java/android/app/slice/views/LargeTemplateView.java index d53e8fcb39e7..9e225162e205 100644 --- a/core/java/android/slice/views/LargeTemplateView.java +++ b/core/java/android/app/slice/views/LargeTemplateView.java @@ -14,22 +14,21 @@ * limitations under the License. */ -package android.slice.views; +package android.app.slice.views; import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; +import android.app.slice.Slice; +import android.app.slice.SliceItem; +import android.app.slice.SliceQuery; +import android.app.slice.views.SliceView.SliceModeView; import android.content.Context; -import android.slice.Slice; -import android.slice.SliceItem; -import android.slice.SliceQuery; -import android.slice.views.SliceView.SliceModeView; import android.util.TypedValue; import com.android.internal.widget.LinearLayoutManager; import com.android.internal.widget.RecyclerView; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; /** @@ -86,7 +85,7 @@ public class LargeTemplateView extends SliceModeView { if (slice.hasHint(Slice.HINT_LIST)) { addList(slice, items); } else { - Arrays.asList(slice.getItems()).forEach(item -> { + slice.getItems().forEach(item -> { if (item.hasHint(Slice.HINT_ACTIONS)) { return; } else if (item.getType() == SliceItem.TYPE_COLOR) { @@ -109,7 +108,7 @@ public class LargeTemplateView extends SliceModeView { } private void addList(Slice slice, List<SliceItem> items) { - List<SliceItem> sliceItems = Arrays.asList(slice.getItems()); + List<SliceItem> sliceItems = slice.getItems(); sliceItems.forEach(i -> i.addHint(Slice.HINT_LIST_ITEM)); items.addAll(sliceItems); } diff --git a/core/java/android/slice/views/MessageView.java b/core/java/android/app/slice/views/MessageView.java index 7b03e0bd92c7..77252bf2bb4b 100644 --- a/core/java/android/slice/views/MessageView.java +++ b/core/java/android/app/slice/views/MessageView.java @@ -14,16 +14,16 @@ * limitations under the License. */ -package android.slice.views; +package android.app.slice.views; +import android.app.slice.Slice; +import android.app.slice.SliceItem; +import android.app.slice.SliceQuery; +import android.app.slice.views.LargeSliceAdapter.SliceListView; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.drawable.Drawable; -import android.slice.Slice; -import android.slice.SliceItem; -import android.slice.SliceQuery; -import android.slice.views.LargeSliceAdapter.SliceListView; import android.text.SpannableStringBuilder; import android.util.AttributeSet; import android.util.TypedValue; diff --git a/core/java/android/slice/views/RemoteInputView.java b/core/java/android/app/slice/views/RemoteInputView.java index a29bb5c0e608..e53cb1ea1764 100644 --- a/core/java/android/slice/views/RemoteInputView.java +++ b/core/java/android/app/slice/views/RemoteInputView.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package android.slice.views; +package android.app.slice.views; import android.animation.Animator; import android.app.Notification; diff --git a/core/java/android/slice/views/ShortcutView.java b/core/java/android/app/slice/views/ShortcutView.java index 8fe2f1ac9e6f..b6790c7df82d 100644 --- a/core/java/android/slice/views/ShortcutView.java +++ b/core/java/android/app/slice/views/ShortcutView.java @@ -14,20 +14,20 @@ * limitations under the License. */ -package android.slice.views; +package android.app.slice.views; import android.app.PendingIntent; import android.app.PendingIntent.CanceledException; +import android.app.slice.Slice; +import android.app.slice.SliceItem; +import android.app.slice.SliceQuery; +import android.app.slice.views.SliceView.SliceModeView; import android.content.Context; import android.content.Intent; import android.graphics.Color; import android.graphics.drawable.ShapeDrawable; import android.graphics.drawable.shapes.OvalShape; import android.net.Uri; -import android.slice.Slice; -import android.slice.SliceItem; -import android.slice.SliceQuery; -import android.slice.views.SliceView.SliceModeView; import android.view.ViewGroup; import com.android.internal.R; diff --git a/core/java/android/slice/views/SliceView.java b/core/java/android/app/slice/views/SliceView.java index f37924816bf2..32484fcabe77 100644 --- a/core/java/android/slice/views/SliceView.java +++ b/core/java/android/app/slice/views/SliceView.java @@ -14,23 +14,25 @@ * limitations under the License. */ -package android.slice.views; +package android.app.slice.views; import android.annotation.StringDef; +import android.app.slice.Slice; +import android.app.slice.SliceItem; +import android.app.slice.SliceQuery; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.graphics.drawable.ColorDrawable; import android.net.Uri; -import android.slice.Slice; -import android.slice.SliceItem; -import android.slice.SliceQuery; import android.util.Log; import android.view.MotionEvent; import android.view.View; import android.widget.FrameLayout; import android.widget.LinearLayout; +import java.util.List; + /** * A view that can display a {@link Slice} in different {@link SliceMode}'s. * @@ -120,7 +122,7 @@ public class SliceView extends LinearLayout { */ public void bindSlice(Uri sliceUri) { validate(sliceUri); - Slice s = mContext.getContentResolver().bindSlice(sliceUri); + Slice s = Slice.bindSlice(mContext.getContentResolver(), sliceUri); bindSlice(s); } @@ -201,7 +203,7 @@ public class SliceView extends LinearLayout { } // TODO: Smarter mapping here from one state to the next. SliceItem color = SliceQuery.find(mCurrentSlice, SliceItem.TYPE_COLOR); - SliceItem[] items = mCurrentSlice.getItems(); + List<SliceItem> items = mCurrentSlice.getItems(); SliceItem actionRow = SliceQuery.find(mCurrentSlice, SliceItem.TYPE_SLICE, Slice.HINT_ACTIONS, Slice.HINT_ALT); @@ -212,7 +214,7 @@ public class SliceView extends LinearLayout { addView(mCurrentView); addView(mActions); } - if (items.length > 1 || (items.length != 0 && items[0] != actionRow)) { + if (items.size() > 1 || (items.size() != 0 && items.get(0) != actionRow)) { mCurrentView.setVisibility(View.VISIBLE); mCurrentView.setSlice(mCurrentSlice); } else { @@ -239,7 +241,7 @@ public class SliceView extends LinearLayout { } private static void validate(Uri sliceUri) { - if (!ContentResolver.SCHEME_SLICE.equals(sliceUri.getScheme())) { + if (!ContentResolver.SCHEME_CONTENT.equals(sliceUri.getScheme())) { throw new RuntimeException("Invalid uri " + sliceUri); } if (sliceUri.getPathSegments().size() == 0) { diff --git a/core/java/android/slice/views/SliceViewUtil.java b/core/java/android/app/slice/views/SliceViewUtil.java index 1b5a6d1e088b..19e8e7c9be4e 100644 --- a/core/java/android/slice/views/SliceViewUtil.java +++ b/core/java/android/app/slice/views/SliceViewUtil.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package android.slice.views; +package android.app.slice.views; import android.annotation.ColorInt; import android.content.Context; diff --git a/core/java/android/slice/views/SmallTemplateView.java b/core/java/android/app/slice/views/SmallTemplateView.java index b0b181ed0169..42b2d21399c9 100644 --- a/core/java/android/slice/views/SmallTemplateView.java +++ b/core/java/android/app/slice/views/SmallTemplateView.java @@ -14,16 +14,16 @@ * limitations under the License. */ -package android.slice.views; +package android.app.slice.views; import android.app.PendingIntent.CanceledException; +import android.app.slice.Slice; +import android.app.slice.SliceItem; +import android.app.slice.SliceQuery; +import android.app.slice.views.LargeSliceAdapter.SliceListView; +import android.app.slice.views.SliceView.SliceModeView; import android.content.Context; import android.os.AsyncTask; -import android.slice.Slice; -import android.slice.SliceItem; -import android.slice.SliceQuery; -import android.slice.views.LargeSliceAdapter.SliceListView; -import android.slice.views.SliceView.SliceModeView; import android.view.View; import android.widget.ImageView; import android.widget.LinearLayout; @@ -34,7 +34,6 @@ import com.android.internal.R; import java.text.Format; import java.text.SimpleDateFormat; import java.util.ArrayList; -import java.util.Arrays; import java.util.Date; import java.util.List; @@ -117,7 +116,7 @@ public class SmallTemplateView extends SliceModeView implements SliceListView { int itemCount = 0; boolean hasSummary = false; ArrayList<SliceItem> sliceItems = new ArrayList<SliceItem>( - Arrays.asList(slice.getSlice().getItems())); + slice.getSlice().getItems()); for (int i = 0; i < sliceItems.size(); i++) { SliceItem item = sliceItems.get(i); if (!hasSummary && item.getType() == SliceItem.TYPE_TEXT @@ -140,9 +139,9 @@ public class SmallTemplateView extends SliceModeView implements SliceListView { mEndContainer.addView(tv); itemCount++; } else if (item.getType() == SliceItem.TYPE_SLICE) { - SliceItem[] subItems = item.getSlice().getItems(); - for (int j = 0; j < subItems.length; j++) { - sliceItems.add(subItems[j]); + List<SliceItem> subItems = item.getSlice().getItems(); + for (int j = 0; j < subItems.size(); j++) { + sliceItems.add(subItems.get(j)); } } } @@ -151,7 +150,8 @@ public class SmallTemplateView extends SliceModeView implements SliceListView { @Override public void setSlice(Slice slice) { - setSliceItem(new SliceItem(slice, SliceItem.TYPE_SLICE, slice.getHints())); + setSliceItem(new SliceItem(slice, SliceItem.TYPE_SLICE, + slice.getHints().toArray(new String[slice.getHints().size()]))); } /** diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java index 051dccbd86c0..fd579fce34d8 100644 --- a/core/java/android/app/usage/UsageStatsManager.java +++ b/core/java/android/app/usage/UsageStatsManager.java @@ -48,10 +48,10 @@ import java.util.Map; * </pre> * A request for data in the middle of a time interval will include that interval. * <p/> - * <b>NOTE:</b> This API requires the permission android.permission.PACKAGE_USAGE_STATS, which - * is a system-level permission and will not be granted to third-party apps. However, declaring - * the permission implies intention to use the API and the user of the device can grant permission - * through the Settings application. + * <b>NOTE:</b> This API requires the permission android.permission.PACKAGE_USAGE_STATS. + * However, declaring the permission implies intention to use the API and the user of the device + * still needs to grant permission through the Settings application. + * See {@link android.provider.Settings#ACTION_USAGE_ACCESS_SETTINGS} */ @SystemService(Context.USAGE_STATS_SERVICE) public final class UsageStatsManager { @@ -122,7 +122,7 @@ public final class UsageStatsManager { * @param intervalType The time interval by which the stats are aggregated. * @param beginTime The inclusive beginning of the range of stats to include in the results. * @param endTime The exclusive end of the range of stats to include in the results. - * @return A list of {@link UsageStats} or null if none are available. + * @return A list of {@link UsageStats} * * @see #INTERVAL_DAILY * @see #INTERVAL_WEEKLY @@ -139,7 +139,7 @@ public final class UsageStatsManager { return slice.getList(); } } catch (RemoteException e) { - // fallthrough and return null. + // fallthrough and return the empty list. } return Collections.emptyList(); } @@ -152,7 +152,7 @@ public final class UsageStatsManager { * @param intervalType The time interval by which the stats are aggregated. * @param beginTime The inclusive beginning of the range of stats to include in the results. * @param endTime The exclusive end of the range of stats to include in the results. - * @return A list of {@link ConfigurationStats} or null if none are available. + * @return A list of {@link ConfigurationStats} */ public List<ConfigurationStats> queryConfigurations(int intervalType, long beginTime, long endTime) { @@ -185,7 +185,7 @@ public final class UsageStatsManager { return iter; } } catch (RemoteException e) { - // fallthrough and return null + // fallthrough and return empty result. } return sEmptyResults; } @@ -197,8 +197,7 @@ public final class UsageStatsManager { * * @param beginTime The inclusive beginning of the range of stats to include in the results. * @param endTime The exclusive end of the range of stats to include in the results. - * @return A {@link java.util.Map} keyed by package name, or null if no stats are - * available. + * @return A {@link java.util.Map} keyed by package name */ public Map<String, UsageStats> queryAndAggregateUsageStats(long beginTime, long endTime) { List<UsageStats> stats = queryUsageStats(INTERVAL_BEST, beginTime, endTime); diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java index 5b2bf456b4e8..cdeaea3ebcae 100644 --- a/core/java/android/content/ContentProvider.java +++ b/core/java/android/content/ContentProvider.java @@ -2099,8 +2099,7 @@ public abstract class ContentProvider implements ComponentCallbacks2 { public static Uri maybeAddUserId(Uri uri, int userId) { if (uri == null) return null; if (userId != UserHandle.USER_CURRENT - && (ContentResolver.SCHEME_CONTENT.equals(uri.getScheme()) - || ContentResolver.SCHEME_SLICE.equals(uri.getScheme()))) { + && ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) { if (!uriHasUserId(uri)) { //We don't add the user Id if there's already one Uri.Builder builder = uri.buildUpon(); diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java index 02e70f55f27e..9ccc552f77f5 100644 --- a/core/java/android/content/ContentResolver.java +++ b/core/java/android/content/ContentResolver.java @@ -47,8 +47,6 @@ import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemClock; import android.os.UserHandle; -import android.slice.Slice; -import android.slice.SliceProvider; import android.text.TextUtils; import android.util.EventLog; import android.util.Log; @@ -180,8 +178,6 @@ public abstract class ContentResolver { public static final Intent ACTION_SYNC_CONN_STATUS_CHANGED = new Intent("com.android.sync.SYNC_CONN_STATUS_CHANGED"); - /** @hide */ - public static final String SCHEME_SLICE = "slice"; public static final String SCHEME_CONTENT = "content"; public static final String SCHEME_ANDROID_RESOURCE = "android.resource"; public static final String SCHEME_FILE = "file"; @@ -1722,36 +1718,6 @@ public abstract class ContentResolver { } /** - * Turns a slice Uri into slice content. - * - * @param uri The URI to a slice provider - * @return The Slice provided by the app or null if none is given. - * @see Slice - * @hide - */ - public final @Nullable Slice bindSlice(@NonNull Uri uri) { - Preconditions.checkNotNull(uri, "uri"); - IContentProvider provider = acquireProvider(uri); - if (provider == null) { - throw new IllegalArgumentException("Unknown URI " + uri); - } - try { - Bundle extras = new Bundle(); - extras.putParcelable(SliceProvider.EXTRA_BIND_URI, uri); - final Bundle res = provider.call(mPackageName, SliceProvider.METHOD_SLICE, null, - extras); - Bundle.setDefusable(res, true); - return res.getParcelable(SliceProvider.EXTRA_SLICE); - } catch (RemoteException e) { - // Arbitrary and not worth documenting, as Activity - // Manager will kill this process shortly anyway. - return null; - } finally { - releaseProvider(provider); - } - } - - /** * Returns the content provider for the given content URI. * * @param uri The URI to a content provider @@ -1759,7 +1725,7 @@ public abstract class ContentResolver { * @hide */ public final IContentProvider acquireProvider(Uri uri) { - if (!SCHEME_CONTENT.equals(uri.getScheme()) && !SCHEME_SLICE.equals(uri.getScheme())) { + if (!SCHEME_CONTENT.equals(uri.getScheme())) { return null; } final String auth = uri.getAuthority(); diff --git a/core/java/android/content/pm/FeatureInfo.java b/core/java/android/content/pm/FeatureInfo.java index 9ee6fa2431a3..ff9fd8ec31c5 100644 --- a/core/java/android/content/pm/FeatureInfo.java +++ b/core/java/android/content/pm/FeatureInfo.java @@ -18,6 +18,7 @@ package android.content.pm; import android.os.Parcel; import android.os.Parcelable; +import android.util.proto.ProtoOutputStream; /** * Definition of a single optional hardware or software feature of an Android @@ -113,6 +114,18 @@ public class FeatureInfo implements Parcelable { dest.writeInt(flags); } + /** @hide */ + public void writeToProto(ProtoOutputStream proto, long fieldId) { + long token = proto.start(fieldId); + if (name != null) { + proto.write(FeatureInfoProto.NAME, name); + } + proto.write(FeatureInfoProto.VERSION, version); + proto.write(FeatureInfoProto.GLES_VERSION, getGlEsVersion()); + proto.write(FeatureInfoProto.FLAGS, flags); + proto.end(token); + } + public static final Creator<FeatureInfo> CREATOR = new Creator<FeatureInfo>() { @Override public FeatureInfo createFromParcel(Parcel source) { diff --git a/core/java/android/os/SystemProperties.java b/core/java/android/os/SystemProperties.java index 560b4b31cdc6..84111fbf7f93 100644 --- a/core/java/android/os/SystemProperties.java +++ b/core/java/android/os/SystemProperties.java @@ -84,9 +84,6 @@ public class SystemProperties { /** * Get the String value for the given {@code key}. * - * <b>WARNING:</b> Do not use this method if the value may not be a valid UTF string! This - * method will crash in native code. - * * @param key the key to lookup * @return an empty string if the {@code key} isn't found */ @@ -99,9 +96,6 @@ public class SystemProperties { /** * Get the String value for the given {@code key}. * - * <b>WARNING:</b> Do not use this method if the value may not be a valid UTF string! This - * method will crash in native code. - * * @param key the key to lookup * @param def the default value in case the property is not set or empty * @return if the {@code key} isn't found, return {@code def} if it isn't null, or an empty diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 53e888176162..b43507465384 100755 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -5708,6 +5708,7 @@ public final class Settings { * * @hide */ + @TestApi public static final String ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED = "accessibility_display_magnification_enabled"; diff --git a/core/java/android/view/ViewDebug.java b/core/java/android/view/ViewDebug.java index 3426485e93ab..afa941316be7 100644 --- a/core/java/android/view/ViewDebug.java +++ b/core/java/android/view/ViewDebug.java @@ -528,84 +528,23 @@ public class ViewDebug { /** @hide */ public static void profileViewAndChildren(final View view, BufferedWriter out) throws IOException { - profileViewAndChildren(view, out, true); + RenderNode node = RenderNode.create("ViewDebug", null); + profileViewAndChildren(view, node, out, true); + node.destroy(); } - private static void profileViewAndChildren(final View view, BufferedWriter out, boolean root) - throws IOException { - + private static void profileViewAndChildren(View view, RenderNode node, BufferedWriter out, + boolean root) throws IOException { long durationMeasure = (root || (view.mPrivateFlags & View.PFLAG_MEASURED_DIMENSION_SET) != 0) - ? profileViewOperation(view, new ViewOperation<Void>() { - public Void[] pre() { - forceLayout(view); - return null; - } - - private void forceLayout(View view) { - view.forceLayout(); - if (view instanceof ViewGroup) { - ViewGroup group = (ViewGroup) view; - final int count = group.getChildCount(); - for (int i = 0; i < count; i++) { - forceLayout(group.getChildAt(i)); - } - } - } - - public void run(Void... data) { - view.measure(view.mOldWidthMeasureSpec, view.mOldHeightMeasureSpec); - } - - public void post(Void... data) { - } - }) - : 0; + ? profileViewMeasure(view) : 0; long durationLayout = (root || (view.mPrivateFlags & View.PFLAG_LAYOUT_REQUIRED) != 0) - ? profileViewOperation(view, new ViewOperation<Void>() { - public Void[] pre() { - return null; - } - - public void run(Void... data) { - view.layout(view.mLeft, view.mTop, view.mRight, view.mBottom); - } - - public void post(Void... data) { - } - }) : 0; + ? profileViewLayout(view) : 0; long durationDraw = (root || !view.willNotDraw() || (view.mPrivateFlags & View.PFLAG_DRAWN) != 0) - ? profileViewOperation(view, new ViewOperation<Object>() { - public Object[] pre() { - final DisplayMetrics metrics = - (view != null && view.getResources() != null) ? - view.getResources().getDisplayMetrics() : null; - final Bitmap bitmap = metrics != null ? - Bitmap.createBitmap(metrics, metrics.widthPixels, - metrics.heightPixels, Bitmap.Config.RGB_565) : null; - final Canvas canvas = bitmap != null ? new Canvas(bitmap) : null; - return new Object[] { - bitmap, canvas - }; - } + ? profileViewDraw(view, node) : 0; - public void run(Object... data) { - if (data[1] != null) { - view.draw((Canvas) data[1]); - } - } - - public void post(Object... data) { - if (data[1] != null) { - ((Canvas) data[1]).setBitmap(null); - } - if (data[0] != null) { - ((Bitmap) data[0]).recycle(); - } - } - }) : 0; out.write(String.valueOf(durationMeasure)); out.write(' '); out.write(String.valueOf(durationLayout)); @@ -616,34 +555,86 @@ public class ViewDebug { ViewGroup group = (ViewGroup) view; final int count = group.getChildCount(); for (int i = 0; i < count; i++) { - profileViewAndChildren(group.getChildAt(i), out, false); + profileViewAndChildren(group.getChildAt(i), node, out, false); + } + } + } + + private static long profileViewMeasure(final View view) { + return profileViewOperation(view, new ViewOperation() { + @Override + public void pre() { + forceLayout(view); + } + + private void forceLayout(View view) { + view.forceLayout(); + if (view instanceof ViewGroup) { + ViewGroup group = (ViewGroup) view; + final int count = group.getChildCount(); + for (int i = 0; i < count; i++) { + forceLayout(group.getChildAt(i)); + } + } + } + + @Override + public void run() { + view.measure(view.mOldWidthMeasureSpec, view.mOldHeightMeasureSpec); + } + }); + } + + private static long profileViewLayout(View view) { + return profileViewOperation(view, + () -> view.layout(view.mLeft, view.mTop, view.mRight, view.mBottom)); + } + + private static long profileViewDraw(View view, RenderNode node) { + DisplayMetrics dm = view.getResources().getDisplayMetrics(); + if (dm == null) { + return 0; + } + + if (view.isHardwareAccelerated()) { + DisplayListCanvas canvas = node.start(dm.widthPixels, dm.heightPixels); + try { + return profileViewOperation(view, () -> view.draw(canvas)); + } finally { + node.end(canvas); + } + } else { + Bitmap bitmap = Bitmap.createBitmap( + dm, dm.widthPixels, dm.heightPixels, Bitmap.Config.RGB_565); + Canvas canvas = new Canvas(bitmap); + try { + return profileViewOperation(view, () -> view.draw(canvas)); + } finally { + canvas.setBitmap(null); + bitmap.recycle(); } } } - interface ViewOperation<T> { - T[] pre(); - void run(T... data); - void post(T... data); + interface ViewOperation { + default void pre() {} + + void run(); } - private static <T> long profileViewOperation(View view, final ViewOperation<T> operation) { + private static long profileViewOperation(View view, final ViewOperation operation) { final CountDownLatch latch = new CountDownLatch(1); final long[] duration = new long[1]; - view.post(new Runnable() { - public void run() { - try { - T[] data = operation.pre(); - long start = Debug.threadCpuTimeNanos(); - //noinspection unchecked - operation.run(data); - duration[0] = Debug.threadCpuTimeNanos() - start; - //noinspection unchecked - operation.post(data); - } finally { - latch.countDown(); - } + view.post(() -> { + try { + operation.pre(); + long start = Debug.threadCpuTimeNanos(); + //noinspection unchecked + operation.run(); + duration[0] = Debug.threadCpuTimeNanos() - start; + } finally { + latch.countDown(); } }); diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java index 44f304dd8781..e564fa344ce5 100644 --- a/core/java/android/view/autofill/AutofillManager.java +++ b/core/java/android/view/autofill/AutofillManager.java @@ -997,7 +997,12 @@ public final class AutofillManager { } private AutofillClient getClientLocked() { - return mContext.getAutofillClient(); + final AutofillClient client = mContext.getAutofillClient(); + if (client == null && sDebug) { + Log.d(TAG, "No AutofillClient for " + mContext.getPackageName() + " on context " + + mContext); + } + return client; } /** @hide */ @@ -1579,6 +1584,7 @@ public final class AutofillManager { final String pfx = outerPrefix + " "; pw.print(pfx); pw.print("sessionId: "); pw.println(mSessionId); pw.print(pfx); pw.print("state: "); pw.println(getStateAsStringLocked()); + pw.print(pfx); pw.print("context: "); pw.println(mContext); pw.print(pfx); pw.print("enabled: "); pw.println(mEnabled); pw.print(pfx); pw.print("hasService: "); pw.println(mService != null); pw.print(pfx); pw.print("hasCallback: "); pw.println(mCallback != null); diff --git a/core/java/android/view/textclassifier/SmartSelection.java b/core/java/android/view/textclassifier/SmartSelection.java index f0e83d1fd85f..2c93a19bbe0e 100644 --- a/core/java/android/view/textclassifier/SmartSelection.java +++ b/core/java/android/view/textclassifier/SmartSelection.java @@ -16,6 +16,8 @@ package android.view.textclassifier; +import android.content.res.AssetFileDescriptor; + /** * Java wrapper for SmartSelection native library interface. * This library is used for detecting entities in text. @@ -42,6 +44,26 @@ final class SmartSelection { } /** + * Creates a new instance of SmartSelect predictor, using the provided model image, given as a + * file path. + */ + SmartSelection(String path) { + mCtx = nativeNewFromPath(path); + } + + /** + * Creates a new instance of SmartSelect predictor, using the provided model image, given as an + * AssetFileDescriptor. + */ + SmartSelection(AssetFileDescriptor afd) { + mCtx = nativeNewFromAssetFileDescriptor(afd, afd.getStartOffset(), afd.getLength()); + if (mCtx == 0L) { + throw new IllegalArgumentException( + "Couldn't initialize TC from given AssetFileDescriptor"); + } + } + + /** * Given a string context and current selection, computes the SmartSelection suggestion. * * The begin and end are character indices into the context UTF8 string. selectionBegin is the @@ -69,6 +91,15 @@ final class SmartSelection { } /** + * Annotates given input text. Every word of the input is a part of some annotation. + * The annotations are sorted by their position in the context string. + * The annotations do not overlap. + */ + public AnnotatedSpan[] annotate(String text) { + return nativeAnnotate(mCtx, text); + } + + /** * Frees up the allocated memory. */ public void close() { @@ -91,12 +122,19 @@ final class SmartSelection { private static native long nativeNew(int fd); + private static native long nativeNewFromPath(String path); + + private static native long nativeNewFromAssetFileDescriptor(AssetFileDescriptor afd, + long offset, long size); + private static native int[] nativeSuggest( long context, String text, int selectionBegin, int selectionEnd); private static native ClassificationResult[] nativeClassifyText( long context, String text, int selectionBegin, int selectionEnd, int hintFlags); + private static native AnnotatedSpan[] nativeAnnotate(long context, String text); + private static native void nativeClose(long context); private static native String nativeGetLanguage(int fd); @@ -114,4 +152,29 @@ final class SmartSelection { mScore = score; } } + + /** Represents a result of Annotate call. */ + public static final class AnnotatedSpan { + final int mStartIndex; + final int mEndIndex; + final ClassificationResult[] mClassification; + + AnnotatedSpan(int startIndex, int endIndex, ClassificationResult[] classification) { + mStartIndex = startIndex; + mEndIndex = endIndex; + mClassification = classification; + } + + public int getStartIndex() { + return mStartIndex; + } + + public int getEndIndex() { + return mEndIndex; + } + + public ClassificationResult[] getClassification() { + return mClassification; + } + } } diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java index 91f6799e26ed..d4be7e5784e0 100644 --- a/core/java/android/widget/Editor.java +++ b/core/java/android/widget/Editor.java @@ -476,6 +476,17 @@ public class Editor { stopTextActionModeWithPreservingSelection(); } + void invalidateMagnifier() { + final DisplayMetrics dm = mTextView.getResources().getDisplayMetrics(); + invalidateMagnifier(0, 0, dm.widthPixels, dm.heightPixels); + } + + void invalidateMagnifier(final float l, final float t, final float r, final float b) { + if (mMagnifier != null) { + mTextView.post(() -> mMagnifier.invalidate(new RectF(l, t, r, b))); + } + } + private void discardTextDisplayLists() { if (mTextRenderNodes != null) { for (int i = 0; i < mTextRenderNodes.length; i++) { @@ -4545,10 +4556,8 @@ public class Editor { + mTextView.getLayout().getLineBottom(lineNumber)) / 2.0f; final int[] coordinatesOnScreen = new int[2]; mTextView.getLocationOnScreen(coordinatesOnScreen); - final float centerXOnScreen = xPosInView + mTextView.getTotalPaddingLeft() - - mTextView.getScrollX() + coordinatesOnScreen[0]; - final float centerYOnScreen = yPosInView + mTextView.getTotalPaddingTop() - - mTextView.getScrollY() + coordinatesOnScreen[1]; + final float centerXOnScreen = mTextView.convertViewToScreenCoord(xPosInView, true); + final float centerYOnScreen = mTextView.convertViewToScreenCoord(yPosInView, false); suspendBlink(); mMagnifier.show(centerXOnScreen, centerYOnScreen, MAGNIFIER_ZOOM); diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index d9bc51fffd6a..ce805526e4ad 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -9219,6 +9219,36 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } } + @Override + public void invalidate() { + super.invalidate(); + + if (mEditor != null) { + mEditor.invalidateMagnifier(); + } + } + + @Override + public void invalidate(int l, int t, int r, int b) { + super.invalidate(l, t, r, b); + + if (mEditor != null) { + mEditor.invalidateMagnifier( + convertViewToScreenCoord(l, true /* isHorizontal */), + convertViewToScreenCoord(t, false /* isHorizontal */), + convertViewToScreenCoord(r, true /* isHorizontal */), + convertViewToScreenCoord(b, false /* isHorizontal */)); + } + } + + float convertViewToScreenCoord(float viewCoord, boolean isHorizontal) { + final int[] coordinatesOnScreen = new int[2]; + getLocationOnScreen(coordinatesOnScreen); + return isHorizontal + ? viewCoord + getTotalPaddingLeft() - getScrollX() + coordinatesOnScreen[0] + : viewCoord + getTotalPaddingTop() - getScrollY() + coordinatesOnScreen[1]; + } + /** * @return whether or not the cursor is visible (assuming this TextView is editable) * diff --git a/core/java/com/android/internal/widget/Magnifier.java b/core/java/com/android/internal/widget/Magnifier.java index 284f2b2b7e12..9bc0778d9be6 100644 --- a/core/java/com/android/internal/widget/Magnifier.java +++ b/core/java/com/android/internal/widget/Magnifier.java @@ -22,7 +22,9 @@ import android.annotation.UiThread; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Point; +import android.graphics.PointF; import android.graphics.Rect; +import android.graphics.RectF; import android.os.Handler; import android.util.Log; import android.view.Gravity; @@ -41,8 +43,8 @@ import com.android.internal.util.Preconditions; */ public final class Magnifier { private static final String LOG_TAG = "magnifier"; - private static final int MINIMUM_MAGNIFIER_SCALE = 1; - private static final int MAXIMUM_MAGNIFIER_SCALE = 4; + // Use this to specify that a previous configuration value does not exist. + private static final int INEXISTENT_PREVIOUS_CONFIG_VALUE = -1; // The view for which this magnifier is attached. private final View mView; // The window containing the magnifier. @@ -61,6 +63,15 @@ public final class Magnifier { // the copy is finished. private final Handler mPixelCopyHandler = Handler.getMain(); + private RectF mTmpRectF; + + // Variables holding previous states, used for detecting redundant calls and invalidation. + private Point mPrevStartCoordsOnScreen = new Point( + INEXISTENT_PREVIOUS_CONFIG_VALUE, INEXISTENT_PREVIOUS_CONFIG_VALUE); + private PointF mPrevCenterCoordsOnScreen = new PointF( + INEXISTENT_PREVIOUS_CONFIG_VALUE, INEXISTENT_PREVIOUS_CONFIG_VALUE); + private float mPrevScale = INEXISTENT_PREVIOUS_CONFIG_VALUE; + /** * Initializes a magnifier. * @@ -90,19 +101,22 @@ public final class Magnifier { /** * Shows the magnifier on the screen. * - * @param centerXOnScreen horizontal coordinate of the center point of the magnifier source - * @param centerYOnScreen vertical coordinate of the center point of the magnifier source - * @param scale the scale at which the magnifier zooms on the source content + * @param centerXOnScreen horizontal coordinate of the center point of the magnifier source. The + * lower end is clamped to 0 + * @param centerYOnScreen vertical coordinate of the center point of the magnifier source. The + * lower end is clamped to 0 + * @param scale the scale at which the magnifier zooms on the source content. The + * lower end is clamped to 1 and the higher end to 4 */ public void show(@FloatRange(from=0) float centerXOnScreen, @FloatRange(from=0) float centerYOnScreen, - @FloatRange(from=MINIMUM_MAGNIFIER_SCALE, to=MAXIMUM_MAGNIFIER_SCALE) float scale) { - if (scale > MAXIMUM_MAGNIFIER_SCALE) { - scale = MAXIMUM_MAGNIFIER_SCALE; + @FloatRange(from=1, to=4) float scale) { + if (scale > 4) { + scale = 4; } - if (scale < MINIMUM_MAGNIFIER_SCALE) { - scale = MINIMUM_MAGNIFIER_SCALE; + if (scale < 1) { + scale = 1; } if (centerXOnScreen < 0) { @@ -113,9 +127,19 @@ public final class Magnifier { centerYOnScreen = 0; } - maybeResizeBitmap(scale); + showInternal(centerXOnScreen, centerYOnScreen, scale, false); + } + + private void showInternal(@FloatRange(from=0) float centerXOnScreen, + @FloatRange(from=0) float centerYOnScreen, + @FloatRange(from=1, to=4) float scale, + boolean forceShow) { + if (mPrevScale != scale) { + resizeBitmap(scale); + mPrevScale = scale; + } configureCoordinates(centerXOnScreen, centerYOnScreen); - performPixelCopy(); + maybePerformPixelCopy(scale, forceShow); if (mWindow.isShowing()) { mWindow.update(mWindowCoords.x, mWindowCoords.y, mWindow.getWidth(), @@ -124,6 +148,9 @@ public final class Magnifier { mWindow.showAtLocation(mView.getRootView(), Gravity.NO_GRAVITY, mWindowCoords.x, mWindowCoords.y); } + + mPrevCenterCoordsOnScreen.x = centerXOnScreen; + mPrevCenterCoordsOnScreen.y = centerYOnScreen; } /** @@ -131,6 +158,38 @@ public final class Magnifier { */ public void dismiss() { mWindow.dismiss(); + + mPrevStartCoordsOnScreen.x = INEXISTENT_PREVIOUS_CONFIG_VALUE; + mPrevStartCoordsOnScreen.y = INEXISTENT_PREVIOUS_CONFIG_VALUE; + mPrevCenterCoordsOnScreen.x = INEXISTENT_PREVIOUS_CONFIG_VALUE; + mPrevCenterCoordsOnScreen.y = INEXISTENT_PREVIOUS_CONFIG_VALUE; + mPrevScale = INEXISTENT_PREVIOUS_CONFIG_VALUE; + } + + /** + * Forces the magnifier to update content by taking and showing a new snapshot using the + * previous coordinates. It does this only if the magnifier is showing and the dirty rectangle + * intersects the rectangle which holds the content to be magnified. + * + * @param dirtyRectOnScreen the rectangle representing the screen bounds of the dirty region + */ + public void invalidate(RectF dirtyRectOnScreen) { + if (mWindow.isShowing() && mPrevCenterCoordsOnScreen.x != INEXISTENT_PREVIOUS_CONFIG_VALUE + && mPrevCenterCoordsOnScreen.y != INEXISTENT_PREVIOUS_CONFIG_VALUE + && mPrevScale != INEXISTENT_PREVIOUS_CONFIG_VALUE) { + // Update the current showing RectF. + mTmpRectF = new RectF(mPrevStartCoordsOnScreen.x, + mPrevStartCoordsOnScreen.y, + mPrevStartCoordsOnScreen.x + mBitmap.getWidth(), + mPrevStartCoordsOnScreen.y + mBitmap.getHeight()); + + // Update only if we are currently showing content that has been declared as invalid. + if (RectF.intersects(dirtyRectOnScreen, mTmpRectF)) { + // Update the contents shown in the magnifier. + showInternal(mPrevCenterCoordsOnScreen.x, mPrevCenterCoordsOnScreen.y, mPrevScale, + true /* forceShow */); + } + } } /** @@ -147,13 +206,11 @@ public final class Magnifier { return mWindowWidth; } - private void maybeResizeBitmap(float scale) { + private void resizeBitmap(float scale) { final int bitmapWidth = (int) (mWindowWidth / scale); final int bitmapHeight = (int) (mWindowHeight / scale); - if (mBitmap.getWidth() != bitmapWidth || mBitmap.getHeight() != bitmapHeight) { - mBitmap.reconfigure(bitmapWidth, bitmapHeight, Bitmap.Config.ARGB_8888); - getImageView().setImageBitmap(mBitmap); - } + mBitmap.reconfigure(bitmapWidth, bitmapHeight, Bitmap.Config.ARGB_8888); + getImageView().setImageBitmap(mBitmap); } private void configureCoordinates(float posXOnScreen, float posYOnScreen) { @@ -166,16 +223,25 @@ public final class Magnifier { mWindowCoords.y = mCenterZoomCoords.y - mWindowHeight / 2 - verticalMagnifierOffset; } - private void performPixelCopy() { - int startX = mCenterZoomCoords.x - mBitmap.getWidth() / 2; + private void maybePerformPixelCopy(final float scale, final boolean forceShow) { + final int startY = mCenterZoomCoords.y - mBitmap.getHeight() / 2; + int rawStartX = mCenterZoomCoords.x - mBitmap.getWidth() / 2; + // Clamp startX value to avoid distorting the rendering of the magnifier content. - if (startX < 0) { - startX = 0; - } else if (startX + mBitmap.getWidth() > mView.getWidth()) { - startX = mView.getWidth() - mBitmap.getWidth(); + if (rawStartX < 0) { + rawStartX = 0; + } else if (rawStartX + mBitmap.getWidth() > mView.getWidth()) { + rawStartX = mView.getWidth() - mBitmap.getWidth(); } - final int startY = mCenterZoomCoords.y - mBitmap.getHeight() / 2; + if (!forceShow && rawStartX == mPrevStartCoordsOnScreen.x + && startY == mPrevStartCoordsOnScreen.y + && scale == mPrevScale) { + // Skip, we are already showing the desired content. + return; + } + + final int startX = rawStartX; final ViewRootImpl viewRootImpl = mView.getViewRootImpl(); if (viewRootImpl != null && viewRootImpl.mSurface != null @@ -185,7 +251,11 @@ public final class Magnifier { new Rect(startX, startY, startX + mBitmap.getWidth(), startY + mBitmap.getHeight()), mBitmap, - result -> getImageView().invalidate(), + result -> { + getImageView().invalidate(); + mPrevStartCoordsOnScreen.x = startX; + mPrevStartCoordsOnScreen.y = startY; + }, mPixelCopyHandler); } else { Log.d(LOG_TAG, "Could not perform PixelCopy request"); diff --git a/core/jni/android_text_Hyphenator.cpp b/core/jni/android_text_Hyphenator.cpp index b46f38959d6b..05bec28a5d39 100644 --- a/core/jni/android_text_Hyphenator.cpp +++ b/core/jni/android_text_Hyphenator.cpp @@ -83,6 +83,7 @@ static void init() { constexpr int INDIC_MIN_SUFFIX = 2; addHyphenator("as", INDIC_MIN_PREFIX, INDIC_MIN_SUFFIX); // Assamese + addHyphenator("be", 2, 2); // Belarusian addHyphenator("bg", 2, 2); // Bulgarian addHyphenator("bn", INDIC_MIN_PREFIX, INDIC_MIN_SUFFIX); // Bengali addHyphenator("cu", 1, 2); // Church Slavonic @@ -106,6 +107,7 @@ static void init() { // Going with a more conservative value of (2, 2) for now. addHyphenator("hy", 2, 2); // Armenian addHyphenator("kn", INDIC_MIN_PREFIX, INDIC_MIN_SUFFIX); // Kannada + addHyphenator("la", 2, 2); // Latin addHyphenator("ml", INDIC_MIN_PREFIX, INDIC_MIN_SUFFIX); // Malayalam addHyphenator("mn-Cyrl", 2, 2); // Mongolian in Cyrillic script addHyphenator("mr", INDIC_MIN_PREFIX, INDIC_MIN_SUFFIX); // Marathi diff --git a/core/proto/android/content/featureinfo.proto b/core/proto/android/content/featureinfo.proto new file mode 100644 index 000000000000..a7501204b43f --- /dev/null +++ b/core/proto/android/content/featureinfo.proto @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * 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. + */ + +syntax = "proto2"; + +option java_package = "android.content.pm"; +option java_multiple_files = true; + +package android.content.pm; + +message FeatureInfoProto { + optional string name = 1; + optional int32 version = 2; + optional string gles_version = 3; + optional int32 flags = 4; +} diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto index c9d7b4966cef..5a5454e2f6ae 100644 --- a/core/proto/android/os/incident.proto +++ b/core/proto/android/os/incident.proto @@ -100,7 +100,11 @@ message IncidentProto { (section).args = "diskstats --proto" ]; - optional android.service.pm.PackageServiceDumpProto package = 3008; + optional android.service.pm.PackageServiceDumpProto package = 3008 [ + (section).type = SECTION_DUMPSYS, + (section).args = "package --proto" + ]; + optional android.service.power.PowerServiceDumpProto power = 3009; optional android.service.print.PrintServiceDumpProto print = 3010; diff --git a/core/proto/android/service/package.proto b/core/proto/android/service/package.proto index be82e2d549f9..aa1a575a5c93 100644 --- a/core/proto/android/service/package.proto +++ b/core/proto/android/service/package.proto @@ -17,6 +17,8 @@ syntax = "proto2"; package android.service.pm; +import "frameworks/base/core/proto/android/content/featureinfo.proto"; + option java_multiple_files = true; option java_outer_classname = "PackageServiceProto"; @@ -36,10 +38,6 @@ message PackageServiceDumpProto { // Should be filled if is_jar is false optional string apk = 4; } - message FeatureProto { - optional string name = 1; - optional int32 version = 2; - } message SharedUserProto { optional int32 user_id = 1; optional string name = 2; @@ -49,7 +47,7 @@ message PackageServiceDumpProto { optional PackageShortProto required_verifier_package = 1; optional PackageShortProto verifier_package = 2; repeated SharedLibraryProto shared_libraries = 3; - repeated FeatureProto features = 4; + repeated android.content.pm.FeatureInfoProto features = 4; repeated PackageProto packages = 5; repeated SharedUserProto shared_users = 6; // Messages from the settings problem file diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 37b5d5d6e206..105bb7ef9f3f 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -3112,7 +3112,7 @@ <!-- Allows an application to bind app's slices and get their content. This content will be surfaced to the user and not to leave the device. - <p>Not for use by third-party applications. @hide --> + <p>Not for use by third-party applications. --> <permission android:name="android.permission.BIND_SLICE" android:protectionLevel="signature|privileged|development" /> diff --git a/core/res/res/layout/slice_grid.xml b/core/res/res/layout/slice_grid.xml index 70df76b0ec60..15ded7b3800e 100644 --- a/core/res/res/layout/slice_grid.xml +++ b/core/res/res/layout/slice_grid.xml @@ -13,7 +13,7 @@ See the License for the specific language governing permissions and limitations under the License. --> -<android.slice.views.GridView +<android.app.slice.views.GridView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" @@ -21,4 +21,4 @@ android:gravity="center_vertical" android:background="?android:attr/activatedBackgroundIndicator" android:clipToPadding="false"> -</android.slice.views.GridView> +</android.app.slice.views.GridView> diff --git a/core/res/res/layout/slice_message.xml b/core/res/res/layout/slice_message.xml index a3279b652c84..96f8078f224d 100644 --- a/core/res/res/layout/slice_message.xml +++ b/core/res/res/layout/slice_message.xml @@ -13,7 +13,7 @@ See the License for the specific language governing permissions and limitations under the License. --> -<android.slice.views.MessageView +<android.app.slice.views.MessageView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" @@ -48,4 +48,4 @@ android:layout_alignStart="@android:id/title" android:textAppearance="?android:attr/textAppearanceListItem" android:maxLines="10" /> -</android.slice.views.MessageView> +</android.app.slice.views.MessageView> diff --git a/core/res/res/layout/slice_message_local.xml b/core/res/res/layout/slice_message_local.xml index d4180f35250b..5c767ba6b8ef 100644 --- a/core/res/res/layout/slice_message_local.xml +++ b/core/res/res/layout/slice_message_local.xml @@ -13,7 +13,7 @@ See the License for the specific language governing permissions and limitations under the License. --> -<android.slice.views.MessageView +<android.app.slice.views.MessageView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" @@ -35,4 +35,4 @@ android:background="#ffeeeeee" android:maxLines="10" /> -</android.slice.views.MessageView> +</android.app.slice.views.MessageView> diff --git a/core/res/res/layout/slice_remote_input.xml b/core/res/res/layout/slice_remote_input.xml index dc570c43ef99..90d0c82be5e7 100644 --- a/core/res/res/layout/slice_remote_input.xml +++ b/core/res/res/layout/slice_remote_input.xml @@ -15,7 +15,7 @@ limitations under the License. --> <!-- LinearLayout --> -<android.slice.views.RemoteInputView +<android.app.slice.views.RemoteInputView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/remote_input" android:background="@drawable/slice_remote_input_bg" @@ -73,4 +73,4 @@ </FrameLayout> -</android.slice.views.RemoteInputView>
\ No newline at end of file +</android.app.slice.views.RemoteInputView>
\ No newline at end of file diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 91c107a3775f..93c02b0e05f6 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -2715,8 +2715,6 @@ <string name="yes">OK</string> <!-- Preference framework strings. --> <string name="no">Cancel</string> - <!-- Preference framework strings. --> - <string name="close">CLOSE</string> <!-- This is the generic "attention" string to be used in attention dialogs. Typically combined with setIconAttribute(android.R.attr.alertDialogIcon) (or setIcon(android.R.drawable.ic_dialog_alert) on legacy versions of the platform) --> @@ -2849,11 +2847,6 @@ <!-- [CHAR LIMIT=200] Compat mode dialog: hint to re-enable compat mode dialog. --> <string name="screen_compat_mode_hint">Re-enable this in System settings > Apps > Downloaded.</string> - <!-- Text of the alert that is displayed when a top application is killed by lmk. --> - <string name="top_app_killed_title">App isn\'t responding</string> - <!-- Top app killed by lmk dialog message. --> - <string name="top_app_killed_message"><xliff:g id="app_name">%1$s</xliff:g> may be using too much memory.</string> - <!-- [CHAR LIMIT=200] Unsupported display size dialog: message. Refers to "Display size" setting. --> <string name="unsupported_display_size_message"><xliff:g id="app_name">%1$s</xliff:g> does not support the current Display size setting and may behave unexpectedly.</string> <!-- [CHAR LIMIT=50] Unsupported display size dialog: check box label. --> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 1307ae2e2c9b..260a5198365b 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -1901,9 +1901,6 @@ <java-symbol type="string" name="anr_application_process" /> <java-symbol type="string" name="anr_process" /> <java-symbol type="string" name="anr_title" /> - <java-symbol type="string" name="top_app_killed_title" /> - <java-symbol type="string" name="top_app_killed_message" /> - <java-symbol type="string" name="close" /> <java-symbol type="string" name="car_mode_disable_notification_message" /> <java-symbol type="string" name="car_mode_disable_notification_title" /> <java-symbol type="string" name="chooser_wallpaper" /> diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp index ad6ce2c4a9d6..40aecac5e27d 100644 --- a/libs/hwui/Android.bp +++ b/libs/hwui/Android.bp @@ -255,11 +255,13 @@ cc_library { // Build libhwui with PGO by default. // Location of PGO profile data is defined in build/soong/cc/pgo.go // and is separate from hwui. - // To turn it off, set ANDROID_PGO_NO_PROFILE_USE environment variable. + // To turn it off, set ANDROID_PGO_NO_PROFILE_USE environment variable + // or set enable_profile_use property to false. pgo: { instrumentation: true, profile_file: "hwui/hwui.profdata", benchmarks: ["hwui"], + enable_profile_use: false, }, } diff --git a/media/java/android/media/MediaMetadataRetriever.java b/media/java/android/media/MediaMetadataRetriever.java index 4ea4e3810e03..760cc49bc1e2 100644 --- a/media/java/android/media/MediaMetadataRetriever.java +++ b/media/java/android/media/MediaMetadataRetriever.java @@ -395,7 +395,7 @@ public class MediaMetadataRetriever * @see #getFrameAtTime(long, int) */ /* Do not change these option values without updating their counterparts - * in include/media/stagefright/MediaSource.h! + * in include/media/MediaSource.h! */ /** * This option is used with {@link #getFrameAtTime(long, int)} to retrieve diff --git a/media/jni/android_media_MediaExtractor.cpp b/media/jni/android_media_MediaExtractor.cpp index d2fa8f5f4c2a..5c90d0020d61 100644 --- a/media/jni/android_media_MediaExtractor.cpp +++ b/media/jni/android_media_MediaExtractor.cpp @@ -37,7 +37,8 @@ #include <media/stagefright/foundation/ABuffer.h> #include <media/stagefright/foundation/ADebug.h> #include <media/stagefright/foundation/AMessage.h> -#include <media/stagefright/DataSource.h> +#include <media/DataSource.h> +#include <media/stagefright/InterfaceUtils.h> #include <media/stagefright/MediaErrors.h> #include <media/stagefright/MetaData.h> #include <media/stagefright/NuMediaExtractor.h> @@ -744,7 +745,7 @@ static void android_media_MediaExtractor_setDataSourceCallback( } sp<DataSource> bridge = - DataSource::CreateFromIDataSource(new JMediaDataSource(env, callbackObj)); + CreateDataSourceFromIDataSource(new JMediaDataSource(env, callbackObj)); status_t err = extractor->setDataSource(bridge); if (err != OK) { diff --git a/media/jni/android_media_MediaExtractor.h b/media/jni/android_media_MediaExtractor.h index 94d36f25ae1c..a4638ac43184 100644 --- a/media/jni/android_media_MediaExtractor.h +++ b/media/jni/android_media_MediaExtractor.h @@ -18,8 +18,8 @@ #define _ANDROID_MEDIA_MEDIAEXTRACTOR_H_ #include <media/stagefright/foundation/ABase.h> -#include <media/stagefright/MediaSource.h> -#include <media/stagefright/DataSource.h> +#include <media/MediaSource.h> +#include <media/DataSource.h> #include <utils/Errors.h> #include <utils/KeyedVector.h> #include <utils/RefBase.h> diff --git a/media/jni/soundpool/SoundPool.cpp b/media/jni/soundpool/SoundPool.cpp index 5f1cf1608942..ab0da072047c 100644 --- a/media/jni/soundpool/SoundPool.cpp +++ b/media/jni/soundpool/SoundPool.cpp @@ -26,7 +26,6 @@ #include <media/AudioTrack.h> #include <media/IMediaHTTPService.h> #include <media/mediaplayer.h> -#include <media/stagefright/MediaExtractor.h> #include "SoundPool.h" #include "SoundPoolThread.h" #include <media/AudioPolicyHelper.h> diff --git a/packages/CtsShim/Android.mk b/packages/CtsShim/Android.mk index fa6423ecb8c7..88b85e078f45 100644 --- a/packages/CtsShim/Android.mk +++ b/packages/CtsShim/Android.mk @@ -30,8 +30,11 @@ LOCAL_BUILT_MODULE_STEM := package.apk # Make sure the build system doesn't try to resign the APK LOCAL_CERTIFICATE := PRESIGNED LOCAL_DEX_PREOPT := false +LOCAL_MODULE_TARGET_ARCH := arm arm64 x86 x86_64 -LOCAL_SRC_FILES := CtsShimPriv.apk +my_archs := arm x86 +my_src_arch := $(call get-prebuilt-src-arch, $(my_archs)) +LOCAL_SRC_FILES := apk/$(my_src_arch)/CtsShimPriv.apk include $(BUILD_PREBUILT) @@ -48,8 +51,11 @@ LOCAL_BUILT_MODULE_STEM := package.apk # Make sure the build system doesn't try to resign the APK LOCAL_CERTIFICATE := PRESIGNED LOCAL_DEX_PREOPT := false +LOCAL_MODULE_TARGET_ARCH := arm arm64 x86 x86_64 -LOCAL_SRC_FILES := CtsShim.apk +my_archs := arm x86 +my_src_arch := $(call get-prebuilt-src-arch, $(my_archs)) +LOCAL_SRC_FILES := apk/$(my_src_arch)/CtsShim.apk include $(BUILD_PREBUILT) diff --git a/packages/CtsShim/CtsShim.apk b/packages/CtsShim/CtsShim.apk Binary files differdeleted file mode 100644 index 27289037dd8b..000000000000 --- a/packages/CtsShim/CtsShim.apk +++ /dev/null diff --git a/packages/CtsShim/CtsShimPriv.apk b/packages/CtsShim/CtsShimPriv.apk Binary files differdeleted file mode 100644 index 9a8e75c28b05..000000000000 --- a/packages/CtsShim/CtsShimPriv.apk +++ /dev/null diff --git a/packages/CtsShim/apk/arm/CtsShim.apk b/packages/CtsShim/apk/arm/CtsShim.apk Binary files differnew file mode 100644 index 000000000000..a91160368cb8 --- /dev/null +++ b/packages/CtsShim/apk/arm/CtsShim.apk diff --git a/packages/CtsShim/apk/arm/CtsShimPriv.apk b/packages/CtsShim/apk/arm/CtsShimPriv.apk Binary files differnew file mode 100644 index 000000000000..845d781f38f3 --- /dev/null +++ b/packages/CtsShim/apk/arm/CtsShimPriv.apk diff --git a/packages/CtsShim/apk/x86/CtsShim.apk b/packages/CtsShim/apk/x86/CtsShim.apk Binary files differnew file mode 100644 index 000000000000..a91160368cb8 --- /dev/null +++ b/packages/CtsShim/apk/x86/CtsShim.apk diff --git a/packages/CtsShim/apk/x86/CtsShimPriv.apk b/packages/CtsShim/apk/x86/CtsShimPriv.apk Binary files differnew file mode 100644 index 000000000000..2fc9a94037fa --- /dev/null +++ b/packages/CtsShim/apk/x86/CtsShimPriv.apk diff --git a/packages/CtsShim/build/README b/packages/CtsShim/build/README index 9869377738b8..59af068f0587 100644 --- a/packages/CtsShim/build/README +++ b/packages/CtsShim/build/README @@ -6,19 +6,34 @@ must specify the singular APK that can be used to upgrade it. NOTE: The need to include a binary on the system image may be deprecated if a solution involving a temporarily writable /system partition is implemented. -build: - $ tapas CtsShim CtsShimPriv CtsShimPrivUpgrade CtsShimPrivUpgradeWrongSHA +For local testing, build the apk and put them in the following folders. +This is for arm: + $ tapas CtsShim CtsShimPriv CtsShimPrivUpgrade CtsShimPrivUpgradeWrongSHA arm64 $ m + $ cp $OUT/system/priv-app/CtsShimPrivUpgrade/CtsShimPrivUpgrade.apk \ + cts/hostsidetests/appsecurity/test-apps/PrivilegedUpdateApp/apk/arm + $ cp $OUT/system/priv-app/CtsShimPrivUpgrade/CtsShimPrivUpgrade.apk \ + vendor/xts/gts-tests/hostsidetests/packagemanager/app/apk/arm/GtsShimPrivUpgrade.apk + $ cp $OUT/system/priv-app/CtsShimPrivUpgradeWrongSHA/CtsShimPrivUpgradeWrongSHA.apk \ + cts/hostsidetests/appsecurity/test-apps/PrivilegedUpdateApp/apk/arm + $ cp $OUT/system/priv-app/CtsShimPriv/CtsShimPriv.apk \ + frameworks/base/packages/CtsShim/apk/arm + $ cp $OUT/system/app/CtsShim/CtsShim.apk \ + frameworks/base/packages/CtsShim/apk/arm -local testing: +This is for x86: + $ tapas CtsShim CtsShimPriv CtsShimPrivUpgrade CtsShimPrivUpgradeWrongSHA x86_64 + $ m + $ cp $OUT/system/priv-app/CtsShimPrivUpgrade/CtsShimPrivUpgrade.apk \ + cts/hostsidetests/appsecurity/test-apps/PrivilegedUpdateApp/apk/x86 $ cp $OUT/system/priv-app/CtsShimPrivUpgrade/CtsShimPrivUpgrade.apk \ - cts/hostsidetests/appsecurity/test-apps/PrivilegedUpdateApp + vendor/xts/gts-tests/hostsidetests/packagemanager/app/apk/x86/GtsShimPrivUpgrade.apk $ cp $OUT/system/priv-app/CtsShimPrivUpgradeWrongSHA/CtsShimPrivUpgradeWrongSHA.apk \ - cts/hostsidetests/appsecurity/test-apps/PrivilegedUpdateApp + cts/hostsidetests/appsecurity/test-apps/PrivilegedUpdateApp/apk/x86 $ cp $OUT/system/priv-app/CtsShimPriv/CtsShimPriv.apk \ - frameworks/base/packages/CtsShim + frameworks/base/packages/CtsShim/apk/x86 $ cp $OUT/system/app/CtsShim/CtsShim.apk \ - frameworks/base/packages/CtsShim + frameworks/base/packages/CtsShim/apk/x86 For final submission, the APKs should be downloaded from the build server, then submitted to the cts/ and frameworks/base/ repos. diff --git a/packages/SettingsLib/src/com/android/settingslib/CustomEditTextPreference.java b/packages/SettingsLib/src/com/android/settingslib/CustomEditTextPreference.java index 253ca11bc44e..90124f1f558f 100644 --- a/packages/SettingsLib/src/com/android/settingslib/CustomEditTextPreference.java +++ b/packages/SettingsLib/src/com/android/settingslib/CustomEditTextPreference.java @@ -20,6 +20,7 @@ import android.app.Dialog; import android.content.Context; import android.content.DialogInterface; import android.os.Bundle; +import android.support.annotation.CallSuper; import android.support.v14.preference.EditTextPreferenceDialogFragment; import android.support.v7.preference.EditTextPreference; import android.util.AttributeSet; @@ -30,7 +31,8 @@ public class CustomEditTextPreference extends EditTextPreference { private CustomPreferenceDialogFragment mFragment; - public CustomEditTextPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { + public CustomEditTextPreference(Context context, AttributeSet attrs, int defStyleAttr, + int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); } @@ -69,7 +71,12 @@ public class CustomEditTextPreference extends EditTextPreference { protected void onClick(DialogInterface dialog, int which) { } + @CallSuper protected void onBindDialogView(View view) { + final EditText editText = view.findViewById(android.R.id.edit); + if (editText != null) { + editText.requestFocus(); + } } private void setFragment(CustomPreferenceDialogFragment fragment) { diff --git a/packages/SettingsLib/src/com/android/settingslib/development/AbstractLogpersistPreferenceController.java b/packages/SettingsLib/src/com/android/settingslib/development/AbstractLogpersistPreferenceController.java index 67553adc528c..77b2d86c445f 100644 --- a/packages/SettingsLib/src/com/android/settingslib/development/AbstractLogpersistPreferenceController.java +++ b/packages/SettingsLib/src/com/android/settingslib/development/AbstractLogpersistPreferenceController.java @@ -68,7 +68,7 @@ public abstract class AbstractLogpersistPreferenceController extends public AbstractLogpersistPreferenceController(Context context, Lifecycle lifecycle) { super(context); - if (isAvailable()) { + if (isAvailable() && lifecycle != null) { lifecycle.addObserver(this); } } diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/CustomEditTextPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/CustomEditTextPreferenceTest.java new file mode 100644 index 000000000000..17c7d13fe67e --- /dev/null +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/CustomEditTextPreferenceTest.java @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settingslib; + +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.view.View; +import android.widget.EditText; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.RuntimeEnvironment; +import org.robolectric.annotation.Config; + +@RunWith(SettingsLibRobolectricTestRunner.class) +@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) +public class CustomEditTextPreferenceTest { + + @Mock + private View mView; + + private TestPreference mPreference; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mPreference = new TestPreference(RuntimeEnvironment.application); + } + + @Test + public void bindDialogView_shouldRequestFocus() { + final String testText = ""; + final EditText editText = spy(new EditText(RuntimeEnvironment.application)); + editText.setText(testText); + when(mView.findViewById(android.R.id.edit)).thenReturn(editText); + + mPreference.onBindDialogView(mView); + + verify(editText).requestFocus(); + } + + private static class TestPreference extends CustomEditTextPreference { + public TestPreference(Context context) { + super(context); + } + } +} diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java index 06d00be8aee8..1557d911e6bf 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java @@ -2319,8 +2319,6 @@ class DatabaseHelper extends SQLiteOpenHelper { loadBooleanSetting(stmt, Settings.System.SCREEN_BRIGHTNESS_MODE, R.bool.def_screen_brightness_automatic_mode); - loadDefaultAnimationSettings(stmt); - loadBooleanSetting(stmt, Settings.System.ACCELEROMETER_ROTATION, R.bool.def_accelerometer_rotation); @@ -2514,6 +2512,8 @@ class DatabaseHelper extends SQLiteOpenHelper { loadSetting(stmt, Settings.Global.MODE_RINGER, AudioManager.RINGER_MODE_NORMAL); + loadDefaultAnimationSettings(stmt); + // --- Previously in 'secure' loadBooleanSetting(stmt, Settings.Global.PACKAGE_VERIFIER_ENABLE, R.bool.def_package_verifier_enable); diff --git a/packages/SystemUI/Android.mk b/packages/SystemUI/Android.mk index 2fd7e87a683e..2c5eb27abe3d 100644 --- a/packages/SystemUI/Android.mk +++ b/packages/SystemUI/Android.mk @@ -31,6 +31,7 @@ LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-Iaidl-files-unde LOCAL_STATIC_ANDROID_LIBRARIES := \ SystemUIPluginLib \ + SystemUISharedLib \ android-support-v4 \ android-support-v7-recyclerview \ android-support-v7-preference \ diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java index b4b4e19028b0..c52c0aae3556 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java @@ -26,6 +26,7 @@ import com.android.systemui.plugins.qs.QSTile.Icon; import com.android.systemui.plugins.qs.QSTile.State; import java.util.Objects; +import java.util.function.Supplier; @ProvidesInterface(version = QSTile.VERSION) @DependsOn(target = QSIconView.class) @@ -104,6 +105,7 @@ public interface QSTile { public static class State { public static final int VERSION = 1; public Icon icon; + public Supplier<Icon> iconSupplier; public int state = Tile.STATE_ACTIVE; public CharSequence label; public CharSequence contentDescription; @@ -118,6 +120,7 @@ public interface QSTile { if (other == null) throw new IllegalArgumentException(); if (!other.getClass().equals(getClass())) throw new IllegalArgumentException(); final boolean changed = !Objects.equals(other.icon, icon) + || !Objects.equals(other.iconSupplier, iconSupplier) || !Objects.equals(other.label, label) || !Objects.equals(other.contentDescription, contentDescription) || !Objects.equals(other.dualLabelContentDescription, @@ -130,6 +133,7 @@ public interface QSTile { || !Objects.equals(other.dualTarget, dualTarget) || !Objects.equals(other.slash, slash); other.icon = icon; + other.iconSupplier = iconSupplier; other.label = label; other.contentDescription = contentDescription; other.dualLabelContentDescription = dualLabelContentDescription; @@ -150,6 +154,7 @@ public interface QSTile { protected StringBuilder toStringBuilder() { final StringBuilder sb = new StringBuilder(getClass().getSimpleName()).append('['); sb.append(",icon=").append(icon); + sb.append(",iconSupplier=").append(iconSupplier); sb.append(",label=").append(label); sb.append(",contentDescription=").append(contentDescription); sb.append(",dualLabelContentDescription=").append(dualLabelContentDescription); diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 7ccb6b0db128..1536b64dc41f 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -451,8 +451,6 @@ <string name="accessibility_recents_item_open_app_info">Open <xliff:g id="app" example="Calendar">%s</xliff:g> application info.</string> <!-- Content description to tell the user an application has been launched from recents --> <string name="accessibility_recents_item_launched">Starting <xliff:g id="app" example="Calendar">%s</xliff:g>.</string> - <!-- Content description of individual recents task. --> - <string name="accessibility_recents_task_header"><xliff:g id="app" example="Chrome">%1$s</xliff:g> <xliff:g id="activity_label" example="www.google.com">%2$s</xliff:g></string> <!-- Content description to tell the user a notification has been removed from the notification shade --> <string name="accessibility_notification_dismissed">Notification dismissed.</string> @@ -810,10 +808,6 @@ <!-- Recents: Accessibility split to the right --> <string name="recents_accessibility_split_screen_right">Split screen to the right</string> - <!-- Fully qualified activity class names to be blacklisted in Recents, add package names into overlay as needed --> - <string-array name="recents_blacklist_array"> - </string-array> - <!-- Expanded Status Bar Header: Battery Charged [CHAR LIMIT=40] --> <string name="expanded_header_battery_charged">Charged</string> diff --git a/packages/SystemUI/shared/Android.mk b/packages/SystemUI/shared/Android.mk new file mode 100644 index 000000000000..88a89bc8eba2 --- /dev/null +++ b/packages/SystemUI/shared/Android.mk @@ -0,0 +1,42 @@ +# Copyright (C) 2017 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_USE_AAPT2 := true + +LOCAL_MODULE_TAGS := optional + +LOCAL_MODULE := SystemUISharedLib + +LOCAL_SRC_FILES := $(call all-java-files-under, src) + +LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res +LOCAL_JAR_EXCLUDE_FILES := none + +include $(BUILD_STATIC_JAVA_LIBRARY) + +include $(CLEAR_VARS) + +LOCAL_PACKAGE_NAME := SharedDummyLib +LOCAL_SRC_FILES := $(call all-java-files-under, src) + +LOCAL_JAVA_LIBRARIES := SystemUISharedLib + +LOCAL_PROGUARD_ENABLED := disabled + +include $(BUILD_PACKAGE) + +include $(call all-makefiles-under,$(LOCAL_PATH))
\ No newline at end of file diff --git a/packages/SystemUI/shared/AndroidManifest.xml b/packages/SystemUI/shared/AndroidManifest.xml new file mode 100644 index 000000000000..43b9c7574141 --- /dev/null +++ b/packages/SystemUI/shared/AndroidManifest.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2017 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.systemui.shared"> + + <uses-sdk + android:minSdkVersion="26" /> + +</manifest> diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/BackgroundTaskLoader.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/BackgroundTaskLoader.java new file mode 100644 index 000000000000..ddd27b0b38ba --- /dev/null +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/BackgroundTaskLoader.java @@ -0,0 +1,186 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.systemui.shared.recents.model; + +import android.content.Context; +import android.content.pm.ActivityInfo; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.os.Handler; +import android.os.HandlerThread; +import android.util.Log; + +import com.android.systemui.shared.system.ActivityManagerWrapper; +import com.android.systemui.shared.system.PackageManagerWrapper; + +/** + * Background task resource loader + */ +class BackgroundTaskLoader implements Runnable { + static String TAG = "BackgroundTaskLoader"; + static boolean DEBUG = false; + + private Context mContext; + private final HandlerThread mLoadThread; + private final Handler mLoadThreadHandler; + private final Handler mMainThreadHandler; + + private final TaskResourceLoadQueue mLoadQueue; + private final TaskKeyLruCache<Drawable> mIconCache; + private final BitmapDrawable mDefaultIcon; + + private boolean mStarted; + private boolean mCancelled; + private boolean mWaitingOnLoadQueue; + + private final OnIdleChangedListener mOnIdleChangedListener; + + /** Constructor, creates a new loading thread that loads task resources in the background */ + public BackgroundTaskLoader(TaskResourceLoadQueue loadQueue, + TaskKeyLruCache<Drawable> iconCache, BitmapDrawable defaultIcon, + OnIdleChangedListener onIdleChangedListener) { + mLoadQueue = loadQueue; + mIconCache = iconCache; + mDefaultIcon = defaultIcon; + mMainThreadHandler = new Handler(); + mOnIdleChangedListener = onIdleChangedListener; + mLoadThread = new HandlerThread("Recents-TaskResourceLoader", + android.os.Process.THREAD_PRIORITY_BACKGROUND); + mLoadThread.start(); + mLoadThreadHandler = new Handler(mLoadThread.getLooper()); + } + + /** Restarts the loader thread */ + void start(Context context) { + mContext = context; + mCancelled = false; + if (!mStarted) { + // Start loading on the load thread + mStarted = true; + mLoadThreadHandler.post(this); + } else { + // Notify the load thread to start loading again + synchronized (mLoadThread) { + mLoadThread.notifyAll(); + } + } + } + + /** Requests the loader thread to stop after the current iteration */ + void stop() { + // Mark as cancelled for the thread to pick up + mCancelled = true; + // If we are waiting for the load queue for more tasks, then we can just reset the + // Context now, since nothing is using it + if (mWaitingOnLoadQueue) { + mContext = null; + } + } + + @Override + public void run() { + while (true) { + if (mCancelled) { + // We have to unset the context here, since the background thread may be using it + // when we call stop() + mContext = null; + // If we are cancelled, then wait until we are started again + synchronized(mLoadThread) { + try { + mLoadThread.wait(); + } catch (InterruptedException ie) { + ie.printStackTrace(); + } + } + } else { + // If we've stopped the loader, then fall through to the above logic to wait on + // the load thread + processLoadQueueItem(); + + // If there are no other items in the list, then just wait until something is added + if (!mCancelled && mLoadQueue.isEmpty()) { + synchronized(mLoadQueue) { + try { + mWaitingOnLoadQueue = true; + mMainThreadHandler.post( + () -> mOnIdleChangedListener.onIdleChanged(true)); + mLoadQueue.wait(); + mMainThreadHandler.post( + () -> mOnIdleChangedListener.onIdleChanged(false)); + mWaitingOnLoadQueue = false; + } catch (InterruptedException ie) { + ie.printStackTrace(); + } + } + } + } + } + } + + /** + * This needs to be in a separate method to work around an surprising interpreter behavior: + * The register will keep the local reference to cachedThumbnailData even if it falls out of + * scope. Putting it into a method fixes this issue. + */ + private void processLoadQueueItem() { + // Load the next item from the queue + final Task t = mLoadQueue.nextTask(); + if (t != null) { + Drawable cachedIcon = mIconCache.get(t.key); + + // Load the icon if it is stale or we haven't cached one yet + if (cachedIcon == null) { + cachedIcon = ActivityManagerWrapper.getInstance().getBadgedTaskDescriptionIcon( + mContext, t.taskDescription, t.key.userId, mContext.getResources()); + + if (cachedIcon == null) { + ActivityInfo info = PackageManagerWrapper.getInstance().getActivityInfo( + t.key.getComponent(), t.key.userId); + if (info != null) { + if (DEBUG) Log.d(TAG, "Loading icon: " + t.key); + cachedIcon = ActivityManagerWrapper.getInstance().getBadgedActivityIcon( + info, t.key.userId); + } + } + + if (cachedIcon == null) { + cachedIcon = mDefaultIcon; + } + + // At this point, even if we can't load the icon, we will set the + // default icon. + mIconCache.put(t.key, cachedIcon); + } + + if (DEBUG) Log.d(TAG, "Loading thumbnail: " + t.key); + final ThumbnailData thumbnailData = + ActivityManagerWrapper.getInstance().getTaskThumbnail(t.key.id, + true /* reducedResolution */); + + if (!mCancelled) { + // Notify that the task data has changed + final Drawable finalIcon = cachedIcon; + mMainThreadHandler.post( + () -> t.notifyTaskDataLoaded(thumbnailData, finalIcon)); + } + } + } + + interface OnIdleChangedListener { + void onIdleChanged(boolean idle); + } +} diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/FilteredTaskList.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/FilteredTaskList.java new file mode 100644 index 000000000000..898d64a1ea1a --- /dev/null +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/FilteredTaskList.java @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.systemui.shared.recents.model; + +import android.util.ArrayMap; +import android.util.SparseArray; + +import com.android.systemui.shared.recents.model.Task.TaskKey; + +import java.util.ArrayList; +import java.util.List; + +/** + * A list of filtered tasks. + */ +class FilteredTaskList { + + private final ArrayList<Task> mTasks = new ArrayList<>(); + private final ArrayList<Task> mFilteredTasks = new ArrayList<>(); + private final ArrayMap<TaskKey, Integer> mFilteredTaskIndices = new ArrayMap<>(); + private TaskFilter mFilter; + + /** Sets the task filter, and returns whether the set of filtered tasks have changed. */ + boolean setFilter(TaskFilter filter) { + ArrayList<Task> prevFilteredTasks = new ArrayList<>(mFilteredTasks); + mFilter = filter; + updateFilteredTasks(); + return !prevFilteredTasks.equals(mFilteredTasks); + } + + /** Adds a new task to the task list */ + void add(Task t) { + mTasks.add(t); + updateFilteredTasks(); + } + + /** Sets the list of tasks */ + void set(List<Task> tasks) { + mTasks.clear(); + mTasks.addAll(tasks); + updateFilteredTasks(); + } + + /** Removes a task from the base list only if it is in the filtered list */ + boolean remove(Task t) { + if (mFilteredTasks.contains(t)) { + boolean removed = mTasks.remove(t); + updateFilteredTasks(); + return removed; + } + return false; + } + + /** Returns the index of this task in the list of filtered tasks */ + int indexOf(Task t) { + if (t != null && mFilteredTaskIndices.containsKey(t.key)) { + return mFilteredTaskIndices.get(t.key); + } + return -1; + } + + /** Returns the size of the list of filtered tasks */ + int size() { + return mFilteredTasks.size(); + } + + /** Returns whether the filtered list contains this task */ + boolean contains(Task t) { + return mFilteredTaskIndices.containsKey(t.key); + } + + /** Updates the list of filtered tasks whenever the base task list changes */ + private void updateFilteredTasks() { + mFilteredTasks.clear(); + if (mFilter != null) { + // Create a sparse array from task id to Task + SparseArray<Task> taskIdMap = new SparseArray<>(); + int taskCount = mTasks.size(); + for (int i = 0; i < taskCount; i++) { + Task t = mTasks.get(i); + taskIdMap.put(t.key.id, t); + } + + for (int i = 0; i < taskCount; i++) { + Task t = mTasks.get(i); + if (mFilter.acceptTask(taskIdMap, t, i)) { + mFilteredTasks.add(t); + } + } + } else { + mFilteredTasks.addAll(mTasks); + } + updateFilteredTaskIndices(); + } + + /** Updates the mapping of tasks to indices. */ + private void updateFilteredTaskIndices() { + int taskCount = mFilteredTasks.size(); + mFilteredTaskIndices.clear(); + for (int i = 0; i < taskCount; i++) { + Task t = mFilteredTasks.get(i); + mFilteredTaskIndices.put(t.key, i); + } + } + + /** Returns the list of filtered tasks */ + ArrayList<Task> getTasks() { + return mFilteredTasks; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/HighResThumbnailLoader.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/HighResThumbnailLoader.java index 6414ea1e9783..24ba99840165 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/model/HighResThumbnailLoader.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/HighResThumbnailLoader.java @@ -14,7 +14,7 @@ * limitations under the License */ -package com.android.systemui.recents.model; +package com.android.systemui.shared.recents.model; import static android.os.Process.setThreadPriority; @@ -25,10 +25,8 @@ import android.util.ArraySet; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; -import com.android.systemui.recents.Recents; -import com.android.systemui.recents.RecentsConfiguration; -import com.android.systemui.recents.misc.SystemServicesProxy; -import com.android.systemui.recents.model.Task.TaskCallbacks; +import com.android.systemui.shared.recents.model.Task.TaskCallbacks; +import com.android.systemui.shared.system.ActivityManagerWrapper; import java.util.ArrayDeque; import java.util.ArrayList; @@ -38,6 +36,8 @@ import java.util.ArrayList; */ public class HighResThumbnailLoader implements TaskCallbacks { + private final ActivityManagerWrapper mActivityManager; + @GuardedBy("mLoadQueue") private final ArrayDeque<Task> mLoadQueue = new ArrayDeque<>(); @GuardedBy("mLoadQueue") @@ -46,20 +46,21 @@ public class HighResThumbnailLoader implements TaskCallbacks { private boolean mLoaderIdling; private final ArrayList<Task> mVisibleTasks = new ArrayList<>(); + private final Thread mLoadThread; private final Handler mMainThreadHandler; - private final SystemServicesProxy mSystemServicesProxy; private final boolean mIsLowRamDevice; private boolean mLoading; private boolean mVisible; private boolean mFlingingFast; private boolean mTaskLoadQueueIdle; - public HighResThumbnailLoader(SystemServicesProxy ssp, Looper looper, boolean isLowRamDevice) { + public HighResThumbnailLoader(ActivityManagerWrapper activityManager, Looper looper, + boolean isLowRamDevice) { + mActivityManager = activityManager; mMainThreadHandler = new Handler(looper); mLoadThread = new Thread(mLoader, "Recents-HighResThumbnailLoader"); mLoadThread.start(); - mSystemServicesProxy = ssp; mIsLowRamDevice = isLowRamDevice; } @@ -220,7 +221,7 @@ public class HighResThumbnailLoader implements TaskCallbacks { } private void loadTask(Task t) { - ThumbnailData thumbnail = mSystemServicesProxy.getTaskThumbnail(t.key.id, + ThumbnailData thumbnail = mActivityManager.getTaskThumbnail(t.key.id, false /* reducedResolution */); mMainThreadHandler.post(() -> { synchronized (mLoadQueue) { diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/RecentsTaskLoadPlan.java index 62ba30bfd5dd..c9368f3ea34c 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/RecentsTaskLoadPlan.java @@ -14,33 +14,21 @@ * limitations under the License. */ -package com.android.systemui.recents.model; +package com.android.systemui.shared.recents.model; import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import android.app.ActivityManager; +import android.app.KeyguardManager; import android.content.Context; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; -import android.content.pm.UserInfo; import android.content.res.Resources; import android.graphics.drawable.Drawable; -import android.os.UserHandle; -import android.os.UserManager; -import android.provider.Settings; -import android.provider.Settings.Secure; -import android.util.ArraySet; -import android.util.SparseArray; import android.util.SparseBooleanArray; -import android.util.SparseIntArray; -import com.android.systemui.Prefs; -import com.android.systemui.R; -import com.android.systemui.recents.Recents; -import com.android.systemui.recents.RecentsDebugFlags; -import com.android.systemui.recents.misc.SystemServicesProxy; -import com.android.systemui.recents.views.lowram.TaskStackLowRamLayoutAlgorithm; -import com.android.systemui.recents.views.grid.TaskGridLayoutAlgorithm; +import com.android.systemui.shared.recents.model.Task.TaskKey; +import com.android.systemui.shared.system.ActivityManagerWrapper; import java.util.ArrayList; import java.util.Collections; @@ -68,14 +56,17 @@ public class RecentsTaskLoadPlan { public int numVisibleTaskThumbnails = 0; } - Context mContext; + private final Context mContext; + private final KeyguardManager mKeyguardManager; - List<ActivityManager.RecentTaskInfo> mRawTasks; - TaskStack mStack; + private List<ActivityManager.RecentTaskInfo> mRawTasks; + private TaskStack mStack; - /** Package level ctor */ - RecentsTaskLoadPlan(Context context) { + private final SparseBooleanArray mTmpLockedUsers = new SparseBooleanArray(); + + public RecentsTaskLoadPlan(Context context) { mContext = context; + mKeyguardManager = (KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE); } /** @@ -85,9 +76,9 @@ public class RecentsTaskLoadPlan { * Note: Do not lock, callers should synchronize on the loader before making this call. */ void preloadRawTasks() { - SystemServicesProxy ssp = Recents.getSystemServices(); - int currentUserId = ssp.getCurrentUser(); - mRawTasks = ssp.getRecentTasks(ActivityManager.getMaxRecentTasksStatic(), currentUserId); + int currentUserId = ActivityManagerWrapper.getInstance().getCurrentUserId(); + mRawTasks = ActivityManagerWrapper.getInstance().getRecentTasks( + ActivityManager.getMaxRecentTasksStatic(), currentUserId); // Since the raw tasks are given in most-recent to least-recent order, we need to reverse it Collections.reverse(mRawTasks); @@ -111,18 +102,13 @@ public class RecentsTaskLoadPlan { preloadRawTasks(); } - SparseBooleanArray lockedUsers = new SparseBooleanArray(); - String dismissDescFormat = mContext.getString( - R.string.accessibility_recents_item_will_be_dismissed); - String appInfoDescFormat = mContext.getString( - R.string.accessibility_recents_item_open_app_info); int taskCount = mRawTasks.size(); for (int i = 0; i < taskCount; i++) { ActivityManager.RecentTaskInfo t = mRawTasks.get(i); // Compose the task key final int windowingMode = t.configuration.windowConfiguration.getWindowingMode(); - Task.TaskKey taskKey = new Task.TaskKey(t.persistentId, windowingMode, t.baseIntent, + TaskKey taskKey = new TaskKey(t.persistentId, windowingMode, t.baseIntent, t.userId, t.lastActiveTime); boolean isFreeformTask = windowingMode == WINDOWING_MODE_FREEFORM; @@ -133,9 +119,7 @@ public class RecentsTaskLoadPlan { ActivityInfo info = loader.getAndUpdateActivityInfo(taskKey); String title = loader.getAndUpdateActivityTitle(taskKey, t.taskDescription); String titleDescription = loader.getAndUpdateContentDescription(taskKey, - t.taskDescription, res); - String dismissDescription = String.format(dismissDescFormat, titleDescription); - String appInfoDescription = String.format(appInfoDescFormat, titleDescription); + t.taskDescription); Drawable icon = isStackTask ? loader.getAndUpdateActivityIcon(taskKey, t.taskDescription, res, false) : null; @@ -145,17 +129,18 @@ public class RecentsTaskLoadPlan { int backgroundColor = loader.getActivityBackgroundColor(t.taskDescription); boolean isSystemApp = (info != null) && ((info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0); - if (lockedUsers.indexOfKey(t.userId) < 0) { - lockedUsers.put(t.userId, Recents.getSystemServices().isDeviceLocked(t.userId)); + + // TODO: Refactor to not do this every preload + if (mTmpLockedUsers.indexOfKey(t.userId) < 0) { + mTmpLockedUsers.put(t.userId, mKeyguardManager.isDeviceLocked(t.userId)); } - boolean isLocked = lockedUsers.get(t.userId); + boolean isLocked = mTmpLockedUsers.get(t.userId); // Add the task to the stack Task task = new Task(taskKey, icon, - thumbnail, title, titleDescription, dismissDescription, appInfoDescription, - activityColor, backgroundColor, isLaunchTarget, isStackTask, isSystemApp, - t.supportsSplitScreenMultiWindow, t.taskDescription, t.resizeMode, - t.topActivity, isLocked); + thumbnail, title, titleDescription, activityColor, backgroundColor, + isLaunchTarget, isStackTask, isSystemApp, t.supportsSplitScreenMultiWindow, + t.taskDescription, t.resizeMode, t.topActivity, isLocked); allTasks.add(task); } @@ -179,7 +164,7 @@ public class RecentsTaskLoadPlan { int taskCount = tasks.size(); for (int i = 0; i < taskCount; i++) { Task task = tasks.get(i); - Task.TaskKey taskKey = task.key; + TaskKey taskKey = task.key; boolean isRunningTask = (task.key.id == opts.runningTaskId); boolean isVisibleTask = i >= (taskCount - opts.numVisibleTasks); @@ -210,13 +195,6 @@ public class RecentsTaskLoadPlan { return mStack; } - /** - * Returns the raw list of recent tasks. - */ - public List<ActivityManager.RecentTaskInfo> getRawTasks() { - return mRawTasks; - } - /** Returns whether there are any tasks in any stacks. */ public boolean hasTasks() { if (mStack != null) { diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/RecentsTaskLoader.java index 3494a00b2e95..de4c72c2ee6a 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/RecentsTaskLoader.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.recents.model; +package com.android.systemui.shared.recents.model; import android.app.ActivityManager; import android.content.ComponentCallbacks2; @@ -25,234 +25,43 @@ import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; -import android.os.Handler; -import android.os.HandlerThread; import android.os.Looper; import android.os.Trace; import android.util.Log; import android.util.LruCache; import com.android.internal.annotations.GuardedBy; -import com.android.systemui.R; -import com.android.systemui.recents.Recents; -import com.android.systemui.recents.RecentsConfiguration; -import com.android.systemui.recents.RecentsDebugFlags; -import com.android.systemui.recents.events.activity.PackagesChangedEvent; -import com.android.systemui.recents.misc.SystemServicesProxy; +import com.android.systemui.shared.recents.model.RecentsTaskLoadPlan.Options; +import com.android.systemui.shared.recents.model.Task.TaskKey; +import com.android.systemui.shared.recents.model.TaskKeyLruCache.EvictionCallback; +import com.android.systemui.shared.system.ActivityManagerWrapper; +import com.android.systemui.shared.system.PackageManagerWrapper; import java.io.PrintWriter; import java.util.Map; -import java.util.concurrent.ConcurrentLinkedQueue; /** - * A Task load queue - */ -class TaskResourceLoadQueue { - - ConcurrentLinkedQueue<Task> mQueue = new ConcurrentLinkedQueue<Task>(); - - /** Adds a new task to the load queue */ - void addTask(Task t) { - if (!mQueue.contains(t)) { - mQueue.add(t); - } - synchronized(this) { - notifyAll(); - } - } - - /** - * Retrieves the next task from the load queue, as well as whether we want that task to be - * force reloaded. - */ - Task nextTask() { - return mQueue.poll(); - } - - /** Removes a task from the load queue */ - void removeTask(Task t) { - mQueue.remove(t); - } - - /** Clears all the tasks from the load queue */ - void clearTasks() { - mQueue.clear(); - } - - /** Returns whether the load queue is empty */ - boolean isEmpty() { - return mQueue.isEmpty(); - } -} - -/** - * Task resource loader - */ -class BackgroundTaskLoader implements Runnable { - static String TAG = "TaskResourceLoader"; - static boolean DEBUG = false; - - Context mContext; - HandlerThread mLoadThread; - Handler mLoadThreadHandler; - Handler mMainThreadHandler; - - TaskResourceLoadQueue mLoadQueue; - TaskKeyLruCache<Drawable> mIconCache; - BitmapDrawable mDefaultIcon; - - boolean mStarted; - boolean mCancelled; - boolean mWaitingOnLoadQueue; - - private final OnIdleChangedListener mOnIdleChangedListener; - - /** Constructor, creates a new loading thread that loads task resources in the background */ - public BackgroundTaskLoader(TaskResourceLoadQueue loadQueue, - TaskKeyLruCache<Drawable> iconCache, BitmapDrawable defaultIcon, - OnIdleChangedListener onIdleChangedListener) { - mLoadQueue = loadQueue; - mIconCache = iconCache; - mDefaultIcon = defaultIcon; - mMainThreadHandler = new Handler(); - mOnIdleChangedListener = onIdleChangedListener; - mLoadThread = new HandlerThread("Recents-TaskResourceLoader", - android.os.Process.THREAD_PRIORITY_BACKGROUND); - mLoadThread.start(); - mLoadThreadHandler = new Handler(mLoadThread.getLooper()); - } - - /** Restarts the loader thread */ - void start(Context context) { - mContext = context; - mCancelled = false; - if (!mStarted) { - // Start loading on the load thread - mStarted = true; - mLoadThreadHandler.post(this); - } else { - // Notify the load thread to start loading again - synchronized (mLoadThread) { - mLoadThread.notifyAll(); - } - } - } - - /** Requests the loader thread to stop after the current iteration */ - void stop() { - // Mark as cancelled for the thread to pick up - mCancelled = true; - // If we are waiting for the load queue for more tasks, then we can just reset the - // Context now, since nothing is using it - if (mWaitingOnLoadQueue) { - mContext = null; - } - } - - @Override - public void run() { - while (true) { - if (mCancelled) { - // We have to unset the context here, since the background thread may be using it - // when we call stop() - mContext = null; - // If we are cancelled, then wait until we are started again - synchronized(mLoadThread) { - try { - mLoadThread.wait(); - } catch (InterruptedException ie) { - ie.printStackTrace(); - } - } - } else { - SystemServicesProxy ssp = Recents.getSystemServices(); - // If we've stopped the loader, then fall through to the above logic to wait on - // the load thread - if (ssp != null) { - processLoadQueueItem(ssp); - } - - // If there are no other items in the list, then just wait until something is added - if (!mCancelled && mLoadQueue.isEmpty()) { - synchronized(mLoadQueue) { - try { - mWaitingOnLoadQueue = true; - mMainThreadHandler.post( - () -> mOnIdleChangedListener.onIdleChanged(true)); - mLoadQueue.wait(); - mMainThreadHandler.post( - () -> mOnIdleChangedListener.onIdleChanged(false)); - mWaitingOnLoadQueue = false; - } catch (InterruptedException ie) { - ie.printStackTrace(); - } - } - } - } - } - } - - /** - * This needs to be in a separate method to work around an surprising interpreter behavior: - * The register will keep the local reference to cachedThumbnailData even if it falls out of - * scope. Putting it into a method fixes this issue. - */ - private void processLoadQueueItem(SystemServicesProxy ssp) { - // Load the next item from the queue - final Task t = mLoadQueue.nextTask(); - if (t != null) { - Drawable cachedIcon = mIconCache.get(t.key); - - // Load the icon if it is stale or we haven't cached one yet - if (cachedIcon == null) { - cachedIcon = ssp.getBadgedTaskDescriptionIcon(t.taskDescription, - t.key.userId, mContext.getResources()); - - if (cachedIcon == null) { - ActivityInfo info = ssp.getActivityInfo( - t.key.getComponent(), t.key.userId); - if (info != null) { - if (DEBUG) Log.d(TAG, "Loading icon: " + t.key); - cachedIcon = ssp.getBadgedActivityIcon(info, t.key.userId); - } - } - - if (cachedIcon == null) { - cachedIcon = mDefaultIcon; - } - - // At this point, even if we can't load the icon, we will set the - // default icon. - mIconCache.put(t.key, cachedIcon); - } - - if (DEBUG) Log.d(TAG, "Loading thumbnail: " + t.key); - final ThumbnailData thumbnailData = ssp.getTaskThumbnail(t.key.id, - true /* reducedResolution */); - - if (!mCancelled) { - // Notify that the task data has changed - final Drawable finalIcon = cachedIcon; - mMainThreadHandler.post( - () -> t.notifyTaskDataLoaded(thumbnailData, finalIcon)); - } - } - } - - interface OnIdleChangedListener { - void onIdleChanged(boolean idle); - } -} - -/** * Recents task loader */ public class RecentsTaskLoader { - private static final String TAG = "RecentsTaskLoader"; private static final boolean DEBUG = false; + /** Levels of svelte in increasing severity/austerity. */ + // No svelting. + public static final int SVELTE_NONE = 0; + // Limit thumbnail cache to number of visible thumbnails when Recents was loaded, disable + // caching thumbnails as you scroll. + public static final int SVELTE_LIMIT_CACHE = 1; + // Disable the thumbnail cache, load thumbnails asynchronously when the activity loads and + // evict all thumbnails when hidden. + public static final int SVELTE_DISABLE_CACHE = 2; + // Disable all thumbnail loading. + public static final int SVELTE_DISABLE_LOADING = 3; + + private final Context mContext; + // This activity info LruCache is useful because it can be expensive to retrieve ActivityInfos // for many tasks, which we use to get the activity labels and icons. Unlike the other caches // below, this is per-package so we can't invalidate the items in the cache based on the last @@ -272,29 +81,27 @@ public class RecentsTaskLoader { private final int mMaxThumbnailCacheSize; private final int mMaxIconCacheSize; private int mNumVisibleTasksLoaded; + private int mSvelteLevel; - int mDefaultTaskBarBackgroundColor; - int mDefaultTaskViewBackgroundColor; - BitmapDrawable mDefaultIcon; + private int mDefaultTaskBarBackgroundColor; + private int mDefaultTaskViewBackgroundColor; + private final BitmapDrawable mDefaultIcon; - private TaskKeyLruCache.EvictionCallback mClearActivityInfoOnEviction = - new TaskKeyLruCache.EvictionCallback() { + private EvictionCallback mClearActivityInfoOnEviction = new EvictionCallback() { @Override - public void onEntryEvicted(Task.TaskKey key) { + public void onEntryEvicted(TaskKey key) { if (key != null) { mActivityInfoCache.remove(key.getComponent()); } } }; - public RecentsTaskLoader(Context context) { - Resources res = context.getResources(); - mDefaultTaskBarBackgroundColor = - context.getColor(R.color.recents_task_bar_default_background_color); - mDefaultTaskViewBackgroundColor = - context.getColor(R.color.recents_task_view_default_background_color); - mMaxThumbnailCacheSize = res.getInteger(R.integer.config_recents_max_thumbnail_count); - mMaxIconCacheSize = res.getInteger(R.integer.config_recents_max_icon_count); + public RecentsTaskLoader(Context context, int maxThumbnailCacheSize, int maxIconCacheSize, + int svelteLevel) { + mContext = context; + mMaxThumbnailCacheSize = maxThumbnailCacheSize; + mMaxIconCacheSize = maxIconCacheSize; + mSvelteLevel = svelteLevel; // Create the default assets Bitmap icon = Bitmap.createBitmap(1, 1, Bitmap.Config.ALPHA_8); @@ -303,18 +110,27 @@ public class RecentsTaskLoader { // Initialize the proxy, cache and loaders int numRecentTasks = ActivityManager.getMaxRecentTasksStatic(); - mHighResThumbnailLoader = new HighResThumbnailLoader(Recents.getSystemServices(), - Looper.getMainLooper(), Recents.getConfiguration().isLowRamDevice); + mHighResThumbnailLoader = new HighResThumbnailLoader(ActivityManagerWrapper.getInstance(), + Looper.getMainLooper(), ActivityManager.isLowRamDeviceStatic()); mLoadQueue = new TaskResourceLoadQueue(); mIconCache = new TaskKeyLruCache<>(mMaxIconCacheSize, mClearActivityInfoOnEviction); mActivityLabelCache = new TaskKeyLruCache<>(numRecentTasks, mClearActivityInfoOnEviction); mContentDescriptionCache = new TaskKeyLruCache<>(numRecentTasks, mClearActivityInfoOnEviction); - mActivityInfoCache = new LruCache(numRecentTasks); + mActivityInfoCache = new LruCache<>(numRecentTasks); mLoader = new BackgroundTaskLoader(mLoadQueue, mIconCache, mDefaultIcon, mHighResThumbnailLoader::setTaskLoadQueueIdle); } + /** + * Sets the default task bar/view colors if none are provided by the app. + */ + public void setDefaultColors(int defaultTaskBarBackgroundColor, + int defaultTaskViewBackgroundColor) { + mDefaultTaskBarBackgroundColor = defaultTaskBarBackgroundColor; + mDefaultTaskViewBackgroundColor = defaultTaskViewBackgroundColor; + } + /** Returns the size of the app icon cache. */ public int getIconCacheSize() { return mMaxIconCacheSize; @@ -329,12 +145,6 @@ public class RecentsTaskLoader { return mHighResThumbnailLoader; } - /** Creates a new plan for loading the recent tasks. */ - public RecentsTaskLoadPlan createLoadPlan(Context context) { - RecentsTaskLoadPlan plan = new RecentsTaskLoadPlan(context); - return plan; - } - /** Preloads recents tasks using the specified plan to store the output. */ public synchronized void preloadTasks(RecentsTaskLoadPlan plan, int runningTaskId) { try { @@ -346,13 +156,11 @@ public class RecentsTaskLoader { } /** Begins loading the heavy task data according to the specified options. */ - public synchronized void loadTasks(Context context, RecentsTaskLoadPlan plan, - RecentsTaskLoadPlan.Options opts) { + public synchronized void loadTasks(RecentsTaskLoadPlan plan, Options opts) { if (opts == null) { throw new RuntimeException("Requires load options"); } if (opts.onlyLoadForCache && opts.loadThumbnails) { - // If we are loading for the cache, we'd like to have the real cache only include the // visible thumbnails. However, we also don't want to reload already cached thumbnails. // Thus, we copy over the current entries into a second cache, and clear the real cache, @@ -453,9 +261,7 @@ public class RecentsTaskLoader { /** * Returns the cached task label if the task key is not expired, updating the cache if it is. */ - String getAndUpdateActivityTitle(Task.TaskKey taskKey, ActivityManager.TaskDescription td) { - SystemServicesProxy ssp = Recents.getSystemServices(); - + String getAndUpdateActivityTitle(TaskKey taskKey, ActivityManager.TaskDescription td) { // Return the task description label if it exists if (td != null && td.getLabel() != null) { return td.getLabel(); @@ -468,7 +274,8 @@ public class RecentsTaskLoader { // All short paths failed, load the label from the activity info and cache it ActivityInfo activityInfo = getAndUpdateActivityInfo(taskKey); if (activityInfo != null) { - label = ssp.getBadgedActivityLabel(activityInfo, taskKey.userId); + label = ActivityManagerWrapper.getInstance().getBadgedActivityLabel(activityInfo, + taskKey.userId); mActivityLabelCache.put(taskKey, label); return label; } @@ -481,10 +288,7 @@ public class RecentsTaskLoader { * Returns the cached task content description if the task key is not expired, updating the * cache if it is. */ - String getAndUpdateContentDescription(Task.TaskKey taskKey, ActivityManager.TaskDescription td, - Resources res) { - SystemServicesProxy ssp = Recents.getSystemServices(); - + String getAndUpdateContentDescription(TaskKey taskKey, ActivityManager.TaskDescription td) { // Return the cached content description if it exists String label = mContentDescriptionCache.getAndInvalidateIfModified(taskKey); if (label != null) { @@ -494,7 +298,8 @@ public class RecentsTaskLoader { // All short paths failed, load the label from the activity info and cache it ActivityInfo activityInfo = getAndUpdateActivityInfo(taskKey); if (activityInfo != null) { - label = ssp.getBadgedContentDescription(activityInfo, taskKey.userId, td, res); + label = ActivityManagerWrapper.getInstance().getBadgedContentDescription( + activityInfo, taskKey.userId, td); if (td == null) { // Only add to the cache if the task description is null, otherwise, it is possible // for the task description to change between calls without the last active time @@ -513,10 +318,8 @@ public class RecentsTaskLoader { /** * Returns the cached task icon if the task key is not expired, updating the cache if it is. */ - Drawable getAndUpdateActivityIcon(Task.TaskKey taskKey, ActivityManager.TaskDescription td, + Drawable getAndUpdateActivityIcon(TaskKey taskKey, ActivityManager.TaskDescription td, Resources res, boolean loadIfNotCached) { - SystemServicesProxy ssp = Recents.getSystemServices(); - // Return the cached activity icon if it exists Drawable icon = mIconCache.getAndInvalidateIfModified(taskKey); if (icon != null) { @@ -525,7 +328,8 @@ public class RecentsTaskLoader { if (loadIfNotCached) { // Return and cache the task description icon if it exists - icon = ssp.getBadgedTaskDescriptionIcon(td, taskKey.userId, res); + icon = ActivityManagerWrapper.getInstance().getBadgedTaskDescriptionIcon(mContext, td, + taskKey.userId, res); if (icon != null) { mIconCache.put(taskKey, icon); return icon; @@ -534,7 +338,8 @@ public class RecentsTaskLoader { // Load the icon from the activity info and cache it ActivityInfo activityInfo = getAndUpdateActivityInfo(taskKey); if (activityInfo != null) { - icon = ssp.getBadgedActivityIcon(activityInfo, taskKey.userId); + icon = ActivityManagerWrapper.getInstance().getBadgedActivityIcon(activityInfo, + taskKey.userId); if (icon != null) { mIconCache.put(taskKey, icon); return icon; @@ -548,10 +353,8 @@ public class RecentsTaskLoader { /** * Returns the cached thumbnail if the task key is not expired, updating the cache if it is. */ - synchronized ThumbnailData getAndUpdateThumbnail(Task.TaskKey taskKey, boolean loadIfNotCached, + synchronized ThumbnailData getAndUpdateThumbnail(TaskKey taskKey, boolean loadIfNotCached, boolean storeInCache) { - SystemServicesProxy ssp = Recents.getSystemServices(); - ThumbnailData cached = mThumbnailCache.getAndInvalidateIfModified(taskKey); if (cached != null) { return cached; @@ -564,11 +367,10 @@ public class RecentsTaskLoader { } if (loadIfNotCached) { - RecentsConfiguration config = Recents.getConfiguration(); - if (config.svelteLevel < RecentsConfiguration.SVELTE_DISABLE_LOADING) { + if (mSvelteLevel < SVELTE_DISABLE_LOADING) { // Load the thumbnail from the system - ThumbnailData thumbnailData = ssp.getTaskThumbnail(taskKey.id, - true /* reducedResolution */); + ThumbnailData thumbnailData = ActivityManagerWrapper.getInstance().getTaskThumbnail( + taskKey.id, true /* reducedResolution */); if (thumbnailData.thumbnail != null) { if (storeInCache) { mThumbnailCache.put(taskKey, thumbnailData); @@ -607,12 +409,11 @@ public class RecentsTaskLoader { * Returns the activity info for the given task key, retrieving one from the system if the * task key is expired. */ - ActivityInfo getAndUpdateActivityInfo(Task.TaskKey taskKey) { - SystemServicesProxy ssp = Recents.getSystemServices(); + ActivityInfo getAndUpdateActivityInfo(TaskKey taskKey) { ComponentName cn = taskKey.getComponent(); ActivityInfo activityInfo = mActivityInfoCache.get(cn); if (activityInfo == null) { - activityInfo = ssp.getActivityInfo(cn, taskKey.userId); + activityInfo = PackageManagerWrapper.getInstance().getActivityInfo(cn, taskKey.userId); if (cn == null || activityInfo == null) { Log.e(TAG, "Unexpected null component name or activity info: " + cn + ", " + activityInfo); diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java index ae417c0c403f..6bddbe01b11b 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java @@ -14,23 +14,17 @@ * limitations under the License. */ -package com.android.systemui.recents.model; +package com.android.systemui.shared.recents.model; -import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; - -import android.app.ActivityManager; import android.app.ActivityManager.TaskDescription; import android.content.ComponentName; import android.content.Intent; import android.content.pm.ActivityInfo; import android.graphics.Color; -import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.view.ViewDebug; -import com.android.systemui.recents.Recents; -import com.android.systemui.recents.misc.SystemServicesProxy; -import com.android.systemui.recents.misc.Utilities; +import com.android.systemui.shared.recents.utilities.Utilities; import java.io.PrintWriter; import java.util.ArrayList; @@ -46,11 +40,11 @@ public class Task { /* Task callbacks */ public interface TaskCallbacks { /* Notifies when a task has been bound */ - public void onTaskDataLoaded(Task task, ThumbnailData thumbnailData); + void onTaskDataLoaded(Task task, ThumbnailData thumbnailData); /* Notifies when a task has been unbound */ - public void onTaskDataUnloaded(); + void onTaskDataUnloaded(); /* Notifies when a task's windowing mode has changed. */ - public void onTaskWindowingModeChanged(); + void onTaskWindowingModeChanged(); } /* The Task Key represents the unique primary key for the task */ @@ -64,8 +58,6 @@ public class Task { @ViewDebug.ExportedProperty(category="recents") public final int userId; @ViewDebug.ExportedProperty(category="recents") - public long firstActiveTime; - @ViewDebug.ExportedProperty(category="recents") public long lastActiveTime; private int mHashCode; @@ -134,10 +126,6 @@ public class Task { @ViewDebug.ExportedProperty(category="recents") public String titleDescription; @ViewDebug.ExportedProperty(category="recents") - public String dismissDescription; - @ViewDebug.ExportedProperty(category="recents") - public String appInfoDescription; - @ViewDebug.ExportedProperty(category="recents") public int colorPrimary; @ViewDebug.ExportedProperty(category="recents") public int colorBackground; @@ -180,17 +168,15 @@ public class Task { } public Task(TaskKey key, Drawable icon, ThumbnailData thumbnail, String title, - String titleDescription, String dismissDescription, String appInfoDescription, - int colorPrimary, int colorBackground, boolean isLaunchTarget, boolean isStackTask, - boolean isSystemApp, boolean isDockable, TaskDescription taskDescription, - int resizeMode, ComponentName topActivity, boolean isLocked) { + String titleDescription, int colorPrimary, int colorBackground, boolean isLaunchTarget, + boolean isStackTask, boolean isSystemApp, boolean isDockable, + TaskDescription taskDescription, int resizeMode, ComponentName topActivity, + boolean isLocked) { this.key = key; this.icon = icon; this.thumbnail = thumbnail; this.title = title; this.titleDescription = titleDescription; - this.dismissDescription = dismissDescription; - this.appInfoDescription = appInfoDescription; this.colorPrimary = colorPrimary; this.colorBackground = colorBackground; this.useLightOnPrimaryColor = Utilities.computeContrastBetweenColors(this.colorPrimary, @@ -214,8 +200,6 @@ public class Task { this.thumbnail = o.thumbnail; this.title = o.title; this.titleDescription = o.titleDescription; - this.dismissDescription = o.dismissDescription; - this.appInfoDescription = o.appInfoDescription; this.colorPrimary = o.colorPrimary; this.colorBackground = o.colorBackground; this.useLightOnPrimaryColor = o.useLightOnPrimaryColor; diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/TaskFilter.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/TaskFilter.java new file mode 100644 index 000000000000..9a1ff544800d --- /dev/null +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/TaskFilter.java @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.systemui.shared.recents.model; + +import android.util.SparseArray; + +/** + * An interface for a task filter to query whether a particular task should show in a stack. + */ +interface TaskFilter { + /** Returns whether the filter accepts the specified task */ + boolean acceptTask(SparseArray<Task> taskIdMap, Task t, int index); +} diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/TaskKeyCache.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/TaskKeyCache.java index 247a654207c8..4bf3500a3405 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/model/TaskKeyCache.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/TaskKeyCache.java @@ -14,12 +14,12 @@ * limitations under the License */ -package com.android.systemui.recents.model; +package com.android.systemui.shared.recents.model; import android.util.Log; import android.util.SparseArray; -import com.android.systemui.recents.model.Task.TaskKey; +import com.android.systemui.shared.recents.model.Task.TaskKey; /** * Base class for both strong and LRU task key cache. @@ -34,7 +34,7 @@ public abstract class TaskKeyCache<V> { * Gets a specific entry in the cache with the specified key, regardless of whether the cached * value is valid or not. */ - final V get(Task.TaskKey key) { + final V get(TaskKey key) { return getCacheEntry(key.id); } @@ -42,8 +42,8 @@ public abstract class TaskKeyCache<V> { * Returns the value only if the key is valid (has not been updated since the last time it was * in the cache) */ - final V getAndInvalidateIfModified(Task.TaskKey key) { - Task.TaskKey lastKey = mKeys.get(key.id); + final V getAndInvalidateIfModified(TaskKey key) { + TaskKey lastKey = mKeys.get(key.id); if (lastKey != null) { if ((lastKey.windowingMode != key.windowingMode) || (lastKey.lastActiveTime != key.lastActiveTime)) { @@ -59,7 +59,7 @@ public abstract class TaskKeyCache<V> { } /** Puts an entry in the cache for a specific key. */ - final void put(Task.TaskKey key, V value) { + final void put(TaskKey key, V value) { if (key == null || value == null) { Log.e(TAG, "Unexpected null key or value: " + key + ", " + value); return; @@ -70,7 +70,7 @@ public abstract class TaskKeyCache<V> { /** Removes a cache entry for a specific key. */ - final void remove(Task.TaskKey key) { + final void remove(TaskKey key) { // Remove the key after the cache value because we need it to make the callback removeCacheEntry(key.id); mKeys.remove(key.id); diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/TaskKeyLruCache.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/TaskKeyLruCache.java index 778df6be399b..0ba2c3bf6e3c 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/model/TaskKeyLruCache.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/TaskKeyLruCache.java @@ -14,14 +14,16 @@ * limitations under the License. */ -package com.android.systemui.recents.model; +package com.android.systemui.shared.recents.model; import android.util.LruCache; +import com.android.systemui.shared.recents.model.Task.TaskKey; + import java.io.PrintWriter; /** - * A mapping of {@link Task.TaskKey} to value, with additional LRU functionality where the least + * A mapping of {@link TaskKey} to value, with additional LRU functionality where the least * recently referenced key/values will be evicted as more values than the given cache size are * inserted. * @@ -31,7 +33,7 @@ import java.io.PrintWriter; public class TaskKeyLruCache<V> extends TaskKeyCache<V> { public interface EvictionCallback { - public void onEntryEvicted(Task.TaskKey key); + void onEntryEvicted(TaskKey key); } private final LruCache<Integer, V> mCache; diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/TaskKeyStrongCache.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/TaskKeyStrongCache.java index c84df8a14288..4408eced3e93 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/model/TaskKeyStrongCache.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/TaskKeyStrongCache.java @@ -14,13 +14,11 @@ * limitations under the License */ -package com.android.systemui.recents.model; +package com.android.systemui.shared.recents.model; import android.util.ArrayMap; -import android.util.Log; -import android.util.SparseArray; -import com.android.systemui.recents.model.Task.TaskKey; +import com.android.systemui.shared.recents.model.Task.TaskKey; import java.io.PrintWriter; diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/TaskResourceLoadQueue.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/TaskResourceLoadQueue.java new file mode 100644 index 000000000000..fbb6acebc8e0 --- /dev/null +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/TaskResourceLoadQueue.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.systemui.shared.recents.model; + +import java.util.concurrent.ConcurrentLinkedQueue; + +/** + * A Task load queue + */ +class TaskResourceLoadQueue { + + private final ConcurrentLinkedQueue<Task> mQueue = new ConcurrentLinkedQueue<>(); + + /** Adds a new task to the load queue */ + void addTask(Task t) { + if (!mQueue.contains(t)) { + mQueue.add(t); + } + synchronized(this) { + notifyAll(); + } + } + + /** + * Retrieves the next task from the load queue, as well as whether we want that task to be + * force reloaded. + */ + Task nextTask() { + return mQueue.poll(); + } + + /** Removes a task from the load queue */ + void removeTask(Task t) { + mQueue.remove(t); + } + + /** Clears all the tasks from the load queue */ + void clearTasks() { + mQueue.clear(); + } + + /** Returns whether the load queue is empty */ + boolean isEmpty() { + return mQueue.isEmpty(); + } +} diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/TaskStack.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/TaskStack.java new file mode 100644 index 000000000000..693379d3ee13 --- /dev/null +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/TaskStack.java @@ -0,0 +1,403 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.shared.recents.model; + +import android.content.ComponentName; +import android.util.ArrayMap; +import android.util.ArraySet; +import android.util.SparseArray; + +import com.android.systemui.shared.recents.model.Task.TaskKey; +import com.android.systemui.shared.recents.utilities.AnimationProps; +import com.android.systemui.shared.system.PackageManagerWrapper; + +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.List; + + +/** + * The task stack contains a list of multiple tasks. + */ +public class TaskStack { + + private static final String TAG = "TaskStack"; + + /** Task stack callbacks */ + public interface TaskStackCallbacks { + /** + * Notifies when a new task has been added to the stack. + */ + void onStackTaskAdded(TaskStack stack, Task newTask); + + /** + * Notifies when a task has been removed from the stack. + */ + void onStackTaskRemoved(TaskStack stack, Task removedTask, Task newFrontMostTask, + AnimationProps animation, boolean fromDockGesture, + boolean dismissRecentsIfAllRemoved); + + /** + * Notifies when all tasks have been removed from the stack. + */ + void onStackTasksRemoved(TaskStack stack); + + /** + * Notifies when tasks in the stack have been updated. + */ + void onStackTasksUpdated(TaskStack stack); + } + + private final ArrayList<Task> mRawTaskList = new ArrayList<>(); + private final FilteredTaskList mStackTaskList = new FilteredTaskList(); + private TaskStackCallbacks mCb; + + public TaskStack() { + // Ensure that we only show stack tasks + mStackTaskList.setFilter((taskIdMap, t, index) -> t.isStackTask); + } + + /** Sets the callbacks for this task stack. */ + public void setCallbacks(TaskStackCallbacks cb) { + mCb = cb; + } + + /** + * Removes a task from the stack, with an additional {@param animation} hint to the callbacks on + * how they should update themselves. + */ + public void removeTask(Task t, AnimationProps animation, boolean fromDockGesture) { + removeTask(t, animation, fromDockGesture, true /* dismissRecentsIfAllRemoved */); + } + + /** + * Removes a task from the stack, with an additional {@param animation} hint to the callbacks on + * how they should update themselves. + */ + public void removeTask(Task t, AnimationProps animation, boolean fromDockGesture, + boolean dismissRecentsIfAllRemoved) { + if (mStackTaskList.contains(t)) { + mStackTaskList.remove(t); + Task newFrontMostTask = getStackFrontMostTask(); + if (mCb != null) { + // Notify that a task has been removed + mCb.onStackTaskRemoved(this, t, newFrontMostTask, animation, + fromDockGesture, dismissRecentsIfAllRemoved); + } + } + mRawTaskList.remove(t); + } + + /** + * Removes all tasks from the stack. + */ + public void removeAllTasks(boolean notifyStackChanges) { + ArrayList<Task> tasks = mStackTaskList.getTasks(); + for (int i = tasks.size() - 1; i >= 0; i--) { + Task t = tasks.get(i); + mStackTaskList.remove(t); + mRawTaskList.remove(t); + } + if (mCb != null && notifyStackChanges) { + // Notify that all tasks have been removed + mCb.onStackTasksRemoved(this); + } + } + + + /** + * @see #setTasks(List, boolean) + */ + public void setTasks(TaskStack stack, boolean notifyStackChanges) { + setTasks(stack.mRawTaskList, notifyStackChanges); + } + + /** + * Sets a few tasks in one go, without calling any callbacks. + * + * @param tasks the new set of tasks to replace the current set. + * @param notifyStackChanges whether or not to callback on specific changes to the list of tasks. + */ + public void setTasks(List<Task> tasks, boolean notifyStackChanges) { + // Compute a has set for each of the tasks + ArrayMap<TaskKey, Task> currentTasksMap = createTaskKeyMapFromList(mRawTaskList); + ArrayMap<TaskKey, Task> newTasksMap = createTaskKeyMapFromList(tasks); + ArrayList<Task> addedTasks = new ArrayList<>(); + ArrayList<Task> removedTasks = new ArrayList<>(); + ArrayList<Task> allTasks = new ArrayList<>(); + + // Disable notifications if there are no callbacks + if (mCb == null) { + notifyStackChanges = false; + } + + // Remove any tasks that no longer exist + int taskCount = mRawTaskList.size(); + for (int i = taskCount - 1; i >= 0; i--) { + Task task = mRawTaskList.get(i); + if (!newTasksMap.containsKey(task.key)) { + if (notifyStackChanges) { + removedTasks.add(task); + } + } + } + + // Add any new tasks + taskCount = tasks.size(); + for (int i = 0; i < taskCount; i++) { + Task newTask = tasks.get(i); + Task currentTask = currentTasksMap.get(newTask.key); + if (currentTask == null && notifyStackChanges) { + addedTasks.add(newTask); + } else if (currentTask != null) { + // The current task has bound callbacks, so just copy the data from the new task + // state and add it back into the list + currentTask.copyFrom(newTask); + newTask = currentTask; + } + allTasks.add(newTask); + } + + // Sort all the tasks to ensure they are ordered correctly + for (int i = allTasks.size() - 1; i >= 0; i--) { + allTasks.get(i).temporarySortIndexInStack = i; + } + + mStackTaskList.set(allTasks); + mRawTaskList.clear(); + mRawTaskList.addAll(allTasks); + + // Only callback for the removed tasks after the stack has updated + int removedTaskCount = removedTasks.size(); + Task newFrontMostTask = getStackFrontMostTask(); + for (int i = 0; i < removedTaskCount; i++) { + mCb.onStackTaskRemoved(this, removedTasks.get(i), newFrontMostTask, + AnimationProps.IMMEDIATE, false /* fromDockGesture */, + true /* dismissRecentsIfAllRemoved */); + } + + // Only callback for the newly added tasks after this stack has been updated + int addedTaskCount = addedTasks.size(); + for (int i = 0; i < addedTaskCount; i++) { + mCb.onStackTaskAdded(this, addedTasks.get(i)); + } + + // Notify that the task stack has been updated + if (notifyStackChanges) { + mCb.onStackTasksUpdated(this); + } + } + + /** + * Gets the front-most task in the stack. + */ + public Task getStackFrontMostTask() { + ArrayList<Task> stackTasks = mStackTaskList.getTasks(); + if (stackTasks.isEmpty()) { + return null; + } + return stackTasks.get(stackTasks.size() - 1); + } + + /** Gets the task keys */ + public ArrayList<TaskKey> getTaskKeys() { + ArrayList<TaskKey> taskKeys = new ArrayList<>(); + ArrayList<Task> tasks = computeAllTasksList(); + int taskCount = tasks.size(); + for (int i = 0; i < taskCount; i++) { + Task task = tasks.get(i); + taskKeys.add(task.key); + } + return taskKeys; + } + + /** + * Returns the set of "active" (non-historical) tasks in the stack that have been used recently. + */ + public ArrayList<Task> getStackTasks() { + return mStackTaskList.getTasks(); + } + + /** + * Computes a set of all the active and historical tasks. + */ + public ArrayList<Task> computeAllTasksList() { + ArrayList<Task> tasks = new ArrayList<>(); + tasks.addAll(mStackTaskList.getTasks()); + return tasks; + } + + /** + * Returns the number of stacktasks. + */ + public int getTaskCount() { + return mStackTaskList.size(); + } + + /** + * Returns the number of stack tasks. + */ + public int getStackTaskCount() { + return mStackTaskList.size(); + } + + /** + * Returns the task in stack tasks which is the launch target. + */ + public Task getLaunchTarget() { + ArrayList<Task> tasks = mStackTaskList.getTasks(); + int taskCount = tasks.size(); + for (int i = 0; i < taskCount; i++) { + Task task = tasks.get(i); + if (task.isLaunchTarget) { + return task; + } + } + return null; + } + + /** + * Returns whether the next launch target should actually be the PiP task. + */ + public boolean isNextLaunchTargetPip(long lastPipTime) { + Task launchTarget = getLaunchTarget(); + Task nextLaunchTarget = getNextLaunchTargetRaw(); + if (nextLaunchTarget != null && lastPipTime > 0) { + // If the PiP time is more recent than the next launch target, then launch the PiP task + return lastPipTime > nextLaunchTarget.key.lastActiveTime; + } else if (launchTarget != null && lastPipTime > 0 && getTaskCount() == 1) { + // Otherwise, if there is no next launch target, but there is a PiP, then launch + // the PiP task + return true; + } + return false; + } + + /** + * Returns the task in stack tasks which should be launched next if Recents are toggled + * again, or null if there is no task to be launched. Callers should check + * {@link #isNextLaunchTargetPip(long)} before fetching the next raw launch target from the + * stack. + */ + public Task getNextLaunchTarget() { + Task nextLaunchTarget = getNextLaunchTargetRaw(); + if (nextLaunchTarget != null) { + return nextLaunchTarget; + } + return getStackTasks().get(getTaskCount() - 1); + } + + private Task getNextLaunchTargetRaw() { + int taskCount = getTaskCount(); + if (taskCount == 0) { + return null; + } + int launchTaskIndex = indexOfStackTask(getLaunchTarget()); + if (launchTaskIndex != -1 && launchTaskIndex > 0) { + return getStackTasks().get(launchTaskIndex - 1); + } + return null; + } + + /** Returns the index of this task in this current task stack */ + public int indexOfStackTask(Task t) { + return mStackTaskList.indexOf(t); + } + + /** Finds the task with the specified task id. */ + public Task findTaskWithId(int taskId) { + ArrayList<Task> tasks = computeAllTasksList(); + int taskCount = tasks.size(); + for (int i = 0; i < taskCount; i++) { + Task task = tasks.get(i); + if (task.key.id == taskId) { + return task; + } + } + return null; + } + + /** + * Computes the components of tasks in this stack that have been removed as a result of a change + * in the specified package. + */ + public ArraySet<ComponentName> computeComponentsRemoved(String packageName, int userId) { + // Identify all the tasks that should be removed as a result of the package being removed. + // Using a set to ensure that we callback once per unique component. + ArraySet<ComponentName> existingComponents = new ArraySet<>(); + ArraySet<ComponentName> removedComponents = new ArraySet<>(); + ArrayList<TaskKey> taskKeys = getTaskKeys(); + int taskKeyCount = taskKeys.size(); + for (int i = 0; i < taskKeyCount; i++) { + TaskKey t = taskKeys.get(i); + + // Skip if this doesn't apply to the current user + if (t.userId != userId) continue; + + ComponentName cn = t.getComponent(); + if (cn.getPackageName().equals(packageName)) { + if (existingComponents.contains(cn)) { + // If we know that the component still exists in the package, then skip + continue; + } + if (PackageManagerWrapper.getInstance().getActivityInfo(cn, userId) != null) { + existingComponents.add(cn); + } else { + removedComponents.add(cn); + } + } + } + return removedComponents; + } + + @Override + public String toString() { + String str = "Stack Tasks (" + mStackTaskList.size() + "):\n"; + ArrayList<Task> tasks = mStackTaskList.getTasks(); + int taskCount = tasks.size(); + for (int i = 0; i < taskCount; i++) { + str += " " + tasks.get(i).toString() + "\n"; + } + return str; + } + + /** + * Given a list of tasks, returns a map of each task's key to the task. + */ + private ArrayMap<TaskKey, Task> createTaskKeyMapFromList(List<Task> tasks) { + ArrayMap<TaskKey, Task> map = new ArrayMap<>(tasks.size()); + int taskCount = tasks.size(); + for (int i = 0; i < taskCount; i++) { + Task task = tasks.get(i); + map.put(task.key, task); + } + return map; + } + + public void dump(String prefix, PrintWriter writer) { + String innerPrefix = prefix + " "; + + writer.print(prefix); writer.print(TAG); + writer.print(" numStackTasks="); writer.print(mStackTaskList.size()); + writer.println(); + ArrayList<Task> tasks = mStackTaskList.getTasks(); + int taskCount = tasks.size(); + for (int i = 0; i < taskCount; i++) { + tasks.get(i).dump(innerPrefix, writer); + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/ThumbnailData.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/ThumbnailData.java index 33ff1b634d64..dd1763bb118b 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/model/ThumbnailData.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/ThumbnailData.java @@ -14,7 +14,9 @@ * limitations under the License. */ -package com.android.systemui.recents.model; +package com.android.systemui.shared.recents.model; + +import static android.content.res.Configuration.ORIENTATION_UNDEFINED; import android.app.ActivityManager.TaskSnapshot; import android.graphics.Bitmap; @@ -25,20 +27,25 @@ import android.graphics.Rect; */ public class ThumbnailData { - // TODO: Make these final once the non-snapshot path is removed. - public Bitmap thumbnail; + public final Bitmap thumbnail; public int orientation; - public final Rect insets = new Rect(); + public Rect insets; public boolean reducedResolution; public float scale; - public static ThumbnailData createFromTaskSnapshot(TaskSnapshot snapshot) { - ThumbnailData out = new ThumbnailData(); - out.thumbnail = Bitmap.createHardwareBitmap(snapshot.getSnapshot()); - out.insets.set(snapshot.getContentInsets()); - out.orientation = snapshot.getOrientation(); - out.reducedResolution = snapshot.isReducedResolution(); - out.scale = snapshot.getScale(); - return out; + public ThumbnailData() { + thumbnail = null; + orientation = ORIENTATION_UNDEFINED; + insets = new Rect(); + reducedResolution = false; + scale = 1f; + } + + public ThumbnailData(TaskSnapshot snapshot) { + thumbnail = Bitmap.createHardwareBitmap(snapshot.getSnapshot()); + insets = new Rect(snapshot.getContentInsets()); + orientation = snapshot.getOrientation(); + reducedResolution = snapshot.isReducedResolution(); + scale = snapshot.getScale(); } } diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/AnimationProps.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/AnimationProps.java index 716d1bcf78c2..2de7f74ba477 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/AnimationProps.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/AnimationProps.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.recents.views; +package com.android.systemui.shared.recents.utilities; import android.animation.Animator; import android.animation.AnimatorSet; @@ -24,8 +24,7 @@ import android.util.SparseArray; import android.util.SparseLongArray; import android.view.View; import android.view.animation.Interpolator; - -import com.android.systemui.Interpolators; +import android.view.animation.LinearInterpolator; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -37,7 +36,8 @@ import java.util.List; */ public class AnimationProps { - public static final AnimationProps IMMEDIATE = new AnimationProps(0, Interpolators.LINEAR); + private static final Interpolator LINEAR_INTERPOLATOR = new LinearInterpolator(); + public static final AnimationProps IMMEDIATE = new AnimationProps(0, LINEAR_INTERPOLATOR); @Retention(RetentionPolicy.SOURCE) @IntDef({ALL, TRANSLATION_X, TRANSLATION_Y, TRANSLATION_Z, ALPHA, SCALE, BOUNDS}) @@ -51,7 +51,6 @@ public class AnimationProps { public static final int SCALE = 5; public static final int BOUNDS = 6; public static final int DIM_ALPHA = 7; - public static final int FOCUS_STATE = 8; private SparseLongArray mPropStartDelay; private SparseLongArray mPropDuration; @@ -195,9 +194,9 @@ public class AnimationProps { if (interp != null) { return interp; } - return mPropInterpolators.get(ALL, Interpolators.LINEAR); + return mPropInterpolators.get(ALL, LINEAR_INTERPOLATOR); } - return Interpolators.LINEAR; + return LINEAR_INTERPOLATOR; } /** diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/RectFEvaluator.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/RectFEvaluator.java index 72511de9ec80..51c1b5aa13d7 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/misc/RectFEvaluator.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/RectFEvaluator.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.systemui.recents.misc; +package com.android.systemui.shared.recents.utilities; import android.animation.TypeEvaluator; import android.graphics.RectF; @@ -23,7 +23,7 @@ import android.graphics.RectF; */ public class RectFEvaluator implements TypeEvaluator<RectF> { - private RectF mRect = new RectF(); + private final RectF mRect = new RectF(); /** * This function returns the result of linearly interpolating the start and diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java index 4349e30f60e0..a5d19639580e 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.recents.misc; +package com.android.systemui.shared.recents.utilities; import android.animation.Animator; import android.animation.AnimatorSet; @@ -38,12 +38,8 @@ import android.view.ViewGroup; import android.view.ViewParent; import android.view.ViewStub; -import com.android.systemui.recents.model.Task; -import com.android.systemui.recents.views.TaskViewTransform; - import java.util.ArrayList; import java.util.Collections; -import java.util.List; /* Common code */ public class Utilities { @@ -76,7 +72,6 @@ public class Utilities { public static final RectFEvaluator RECTF_EVALUATOR = new RectFEvaluator(); public static final RectEvaluator RECT_EVALUATOR = new RectEvaluator(new Rect()); - public static final Rect EMPTY_RECT = new Rect(); /** * @return the first parent walking up the view hierarchy that has the given class type. @@ -253,24 +248,6 @@ public class Utilities { } /** - * Updates {@param transforms} to be the same size as {@param tasks}. - */ - public static void matchTaskListSize(List<Task> tasks, List<TaskViewTransform> transforms) { - // We can reuse the task transforms where possible to reduce object allocation - int taskTransformCount = transforms.size(); - int taskCount = tasks.size(); - if (taskTransformCount < taskCount) { - // If there are less transforms than tasks, then add as many transforms as necessary - for (int i = taskTransformCount; i < taskCount; i++) { - transforms.add(new TaskViewTransform()); - } - } else if (taskTransformCount > taskCount) { - // If there are more transforms than tasks, then just subset the transform list - transforms.subList(taskCount, taskTransformCount).clear(); - } - } - - /** * Used for debugging, converts DP to PX. */ public static float dpToPx(Resources res, float dp) { diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java new file mode 100644 index 000000000000..3f93f76af7e4 --- /dev/null +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java @@ -0,0 +1,201 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.shared.system; + +import static android.app.ActivityManager.RECENT_IGNORE_UNAVAILABLE; + +import android.annotation.NonNull; +import android.app.ActivityManager; +import android.app.ActivityManager.RecentTaskInfo; +import android.app.AppGlobals; +import android.content.Context; +import android.content.pm.ActivityInfo; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.content.pm.UserInfo; +import android.content.res.Resources; +import android.content.res.Resources.NotFoundException; +import android.graphics.Bitmap; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.os.RemoteException; +import android.os.UserHandle; +import android.util.IconDrawableFactory; +import android.util.Log; + +import com.android.systemui.shared.recents.model.ThumbnailData; + +import java.util.ArrayList; +import java.util.List; + +public class ActivityManagerWrapper { + + private static final String TAG = "ActivityManagerWrapper"; + + private static final ActivityManagerWrapper sInstance = new ActivityManagerWrapper(); + + private final PackageManager mPackageManager; + private final IconDrawableFactory mDrawableFactory; + + private ActivityManagerWrapper() { + final Context context = AppGlobals.getInitialApplication(); + mPackageManager = context.getPackageManager(); + mDrawableFactory = IconDrawableFactory.newInstance(context); + } + + public static ActivityManagerWrapper getInstance() { + return sInstance; + } + + /** + * @return the current user's id. + */ + public int getCurrentUserId() { + UserInfo ui; + try { + ui = ActivityManager.getService().getCurrentUser(); + return ui != null ? ui.id : 0; + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * @return a list of the recents tasks. + */ + public List<RecentTaskInfo> getRecentTasks(int numTasks, int userId) { + try { + return ActivityManager.getService().getRecentTasks(numTasks, + RECENT_IGNORE_UNAVAILABLE, userId).getList(); + } catch (RemoteException e) { + Log.e(TAG, "Failed to get recent tasks", e); + return new ArrayList<>(); + } + } + + /** + * @return the task snapshot for the given {@param taskId}. + */ + public @NonNull ThumbnailData getTaskThumbnail(int taskId, boolean reducedResolution) { + ActivityManager.TaskSnapshot snapshot = null; + try { + snapshot = ActivityManager.getService().getTaskSnapshot(taskId, reducedResolution); + } catch (RemoteException e) { + Log.w(TAG, "Failed to retrieve task snapshot", e); + } + if (snapshot != null) { + return new ThumbnailData(snapshot); + } else { + return new ThumbnailData(); + } + } + + /** + * @return the task description icon, loading and badging it if it necessary. + */ + public Drawable getBadgedTaskDescriptionIcon(Context context, + ActivityManager.TaskDescription taskDescription, int userId, Resources res) { + Bitmap tdIcon = taskDescription.getInMemoryIcon(); + Drawable dIcon = null; + if (tdIcon != null) { + dIcon = new BitmapDrawable(res, tdIcon); + } else if (taskDescription.getIconResource() != 0) { + try { + dIcon = context.getDrawable(taskDescription.getIconResource()); + } catch (NotFoundException e) { + Log.e(TAG, "Could not find icon drawable from resource", e); + } + } else { + tdIcon = ActivityManager.TaskDescription.loadTaskDescriptionIcon( + taskDescription.getIconFilename(), userId); + if (tdIcon != null) { + dIcon = new BitmapDrawable(res, tdIcon); + } + } + if (dIcon != null) { + return getBadgedIcon(dIcon, userId); + } + return null; + } + + /** + * @return the given icon for a user, badging if necessary. + */ + private Drawable getBadgedIcon(Drawable icon, int userId) { + if (userId != UserHandle.myUserId()) { + icon = mPackageManager.getUserBadgedIcon(icon, new UserHandle(userId)); + } + return icon; + } + + /** + * @return the activity icon for the ActivityInfo for a user, badging if necessary. + */ + public Drawable getBadgedActivityIcon(ActivityInfo info, int userId) { + return mDrawableFactory.getBadgedIcon(info, info.applicationInfo, userId); + } + + /** + * @return the application icon for the ApplicationInfo for a user, badging if necessary. + */ + public Drawable getBadgedApplicationIcon(ApplicationInfo appInfo, int userId) { + return mDrawableFactory.getBadgedIcon(appInfo, userId); + } + + /** + * @return the activity label, badging if necessary. + */ + public String getBadgedActivityLabel(ActivityInfo info, int userId) { + return getBadgedLabel(info.loadLabel(mPackageManager).toString(), userId); + } + + /** + * @return the application label, badging if necessary. + */ + public String getBadgedApplicationLabel(ApplicationInfo appInfo, int userId) { + return getBadgedLabel(appInfo.loadLabel(mPackageManager).toString(), userId); + } + + /** + * @return the content description for a given task, badging it if necessary. The content + * description joins the app and activity labels. + */ + public String getBadgedContentDescription(ActivityInfo info, int userId, + ActivityManager.TaskDescription td) { + String activityLabel; + if (td != null && td.getLabel() != null) { + activityLabel = td.getLabel(); + } else { + activityLabel = info.loadLabel(mPackageManager).toString(); + } + String applicationLabel = info.applicationInfo.loadLabel(mPackageManager).toString(); + String badgedApplicationLabel = getBadgedLabel(applicationLabel, userId); + return applicationLabel.equals(activityLabel) + ? badgedApplicationLabel + : badgedApplicationLabel + " " + activityLabel; + } + + /** + * @return the given label for a user, badging if necessary. + */ + private String getBadgedLabel(String label, int userId) { + if (userId != UserHandle.myUserId()) { + label = mPackageManager.getUserBadgedLabel(label, new UserHandle(userId)).toString(); + } + return label; + } +} diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/PackageManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/PackageManagerWrapper.java new file mode 100644 index 000000000000..d5e6e6efb3cf --- /dev/null +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/PackageManagerWrapper.java @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.shared.system; + +import android.app.AppGlobals; +import android.content.ComponentName; +import android.content.pm.ActivityInfo; +import android.content.pm.IPackageManager; +import android.content.pm.PackageManager; +import android.os.RemoteException; + +public class PackageManagerWrapper { + + private static final String TAG = "PackageManagerWrapper"; + + private static final PackageManagerWrapper sInstance = new PackageManagerWrapper(); + + private static final IPackageManager mIPackageManager = AppGlobals.getPackageManager(); + + public static PackageManagerWrapper getInstance() { + return sInstance; + } + + /** + * @return the activity info for a given {@param componentName} and {@param userId}. + */ + public ActivityInfo getActivityInfo(ComponentName componentName, int userId) { + try { + return mIPackageManager.getActivityInfo(componentName, PackageManager.GET_META_DATA, + userId); + } catch (RemoteException e) { + e.printStackTrace(); + return null; + } + } +} diff --git a/packages/SystemUI/shared/tests/Android.mk b/packages/SystemUI/shared/tests/Android.mk new file mode 100644 index 000000000000..ce3b4424de50 --- /dev/null +++ b/packages/SystemUI/shared/tests/Android.mk @@ -0,0 +1,53 @@ +# Copyright (C) 2017 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_USE_AAPT2 := true +LOCAL_MODULE_TAGS := tests + +LOCAL_JACK_FLAGS := --multi-dex native +LOCAL_DX_FLAGS := --multi-dex + +LOCAL_PROTOC_OPTIMIZE_TYPE := nano +LOCAL_PROTOC_FLAGS := -I$(LOCAL_PATH)/.. +LOCAL_PROTO_JAVA_OUTPUT_PARAMS := optional_field_style=accessors + +LOCAL_PACKAGE_NAME := SystemUISharedLibTests +LOCAL_COMPATIBILITY_SUITE := device-tests + +# Add local path sources as well as shared lib sources +LOCAL_SRC_FILES := $(call all-java-files-under, src) \ + $(call all-java-files-under, ../src) + +LOCAL_STATIC_JAVA_LIBRARIES := \ + metrics-helper-lib \ + android-support-test \ + mockito-target-minus-junit4 \ + SystemUI-proto \ + SystemUI-tags \ + legacy-android-test \ + testables \ + truth-prebuilt \ + +LOCAL_JAVA_LIBRARIES := android.test.runner telephony-common android.car + +# sign this with platform cert, so this test is allowed to inject key events into +# UI it doesn't own. This is necessary to allow screenshots to be taken +LOCAL_CERTIFICATE := platform + +ifeq ($(EXCLUDE_SYSTEMUI_TESTS),) + include $(BUILD_PACKAGE) +endif
\ No newline at end of file diff --git a/packages/SystemUI/shared/tests/AndroidManifest.xml b/packages/SystemUI/shared/tests/AndroidManifest.xml new file mode 100644 index 000000000000..3e1de499c801 --- /dev/null +++ b/packages/SystemUI/shared/tests/AndroidManifest.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2017 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.systemui.shared.tests"> + + <uses-permission android:name="android.permission.READ_FRAME_BUFFER" /> + + <application> + <uses-library android:name="android.test.runner" /> + </application> + + <instrumentation android:name="android.testing.TestableInstrumentation" + android:targetPackage="com.android.systemui.shared.tests" + android:label="Tests for SystemUISharedLib"> + </instrumentation> +</manifest> diff --git a/packages/SystemUI/shared/tests/AndroidTest.xml b/packages/SystemUI/shared/tests/AndroidTest.xml new file mode 100644 index 000000000000..b3de8368deec --- /dev/null +++ b/packages/SystemUI/shared/tests/AndroidTest.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2017 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<configuration description="Runs Tests for SystemUISharedLib."> + <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup"> + <option name="test-file-name" value="SystemUISharedLibTests.apk" /> + </target_preparer> + + <option name="test-suite-tag" value="apct" /> + <option name="test-suite-tag" value="framework-base-presubmit" /> + <option name="test-tag" value="SystemUISharedLibTests" /> + <test class="com.android.tradefed.testtype.AndroidJUnitTest" > + <option name="package" value="com.android.systemui.shared.tests" /> + <option name="runner" value="android.testing.TestableInstrumentation" /> + </test> +</configuration> diff --git a/packages/SystemUI/shared/tests/src/com/android/systemui/shared/SysuiSharedLibTestCase.java b/packages/SystemUI/shared/tests/src/com/android/systemui/shared/SysuiSharedLibTestCase.java new file mode 100644 index 000000000000..04b341e38c04 --- /dev/null +++ b/packages/SystemUI/shared/tests/src/com/android/systemui/shared/SysuiSharedLibTestCase.java @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.shared; + +import android.content.Context; +import android.os.Handler; +import android.os.Looper; +import android.os.MessageQueue; +import android.support.test.InstrumentationRegistry; + +import org.junit.After; +import org.junit.Before; + +/** + * Base class that does System UI Shared Lib specific setup. + */ +public abstract class SysuiSharedLibTestCase { + + private static final String TAG = "SysuiSharedLibTestCase"; + + private Handler mHandler; + private Context mContext = InstrumentationRegistry.getContext(); + + @Before + public void SysuiSetup() throws Exception { + // Enable shared class loader to test package-private classes/methods + System.setProperty("dexmaker.share_classloader", "true"); + } + + @After + public void SysuiTeardown() { + // Do nothing + } + + public Context getContext() { + return mContext; + } + + protected void waitForIdleSync() { + if (mHandler == null) { + mHandler = new Handler(Looper.getMainLooper()); + } + waitForIdleSync(mHandler); + } + + public static void waitForIdleSync(Handler h) { + validateThread(h.getLooper()); + Idler idler = new Idler(null); + h.getLooper().getQueue().addIdleHandler(idler); + // Ensure we are non-idle, so the idle handler can run. + h.post(new EmptyRunnable()); + idler.waitForIdle(); + } + + private static final void validateThread(Looper l) { + if (Looper.myLooper() == l) { + throw new RuntimeException( + "This method can not be called from the looper being synced"); + } + } + + public static final class EmptyRunnable implements Runnable { + public void run() { + } + } + + public static final class Idler implements MessageQueue.IdleHandler { + private final Runnable mCallback; + private boolean mIdle; + + public Idler(Runnable callback) { + mCallback = callback; + mIdle = false; + } + + @Override + public boolean queueIdle() { + if (mCallback != null) { + mCallback.run(); + } + synchronized (this) { + mIdle = true; + notifyAll(); + } + return false; + } + + public void waitForIdle() { + synchronized (this) { + while (!mIdle) { + try { + wait(); + } catch (InterruptedException e) { + } + } + } + } + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/recents/model/HighResThumbnailLoaderTest.java b/packages/SystemUI/shared/tests/src/com/android/systemui/shared/recents/model/HighResThumbnailLoaderTest.java index 4564c8c79814..b03ea90fb1db 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/recents/model/HighResThumbnailLoaderTest.java +++ b/packages/SystemUI/shared/tests/src/com/android/systemui/shared/recents/model/HighResThumbnailLoaderTest.java @@ -14,9 +14,10 @@ * limitations under the License */ -package com.android.systemui.recents.model; +package com.android.systemui.shared.recents.model; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; + import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.anyBoolean; @@ -29,9 +30,9 @@ import android.os.Looper; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; -import com.android.systemui.SysuiTestCase; -import com.android.systemui.recents.misc.SystemServicesProxy; -import com.android.systemui.recents.model.Task.TaskKey; +import com.android.systemui.shared.SysuiSharedLibTestCase; +import com.android.systemui.shared.recents.model.Task.TaskKey; +import com.android.systemui.shared.system.ActivityManagerWrapper; import org.junit.Before; import org.junit.Test; @@ -40,16 +41,16 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; /** - * runtest systemui -c com.android.systemui.recents.model.HighResThumbnailLoaderTest + * runtest --path frameworks/base/packages/SystemUI/shared/tests/src/com/android/systemui/shared/recents/model/HighResThumbnailLoaderTest.java */ @SmallTest @RunWith(AndroidJUnit4.class) -public class HighResThumbnailLoaderTest extends SysuiTestCase { +public class HighResThumbnailLoaderTest extends SysuiSharedLibTestCase { private HighResThumbnailLoader mLoader; @Mock - private SystemServicesProxy mMockSystemServicesProxy; + private ActivityManagerWrapper mMockActivityManagerWrapper; @Mock private Task mTask; @@ -58,10 +59,10 @@ public class HighResThumbnailLoaderTest extends SysuiTestCase { @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); - mLoader = new HighResThumbnailLoader(mMockSystemServicesProxy, Looper.getMainLooper(), - false); + mLoader = new HighResThumbnailLoader(mMockActivityManagerWrapper, Looper.getMainLooper(), + false /* reducedResolution */); mTask.key = new TaskKey(0, WINDOWING_MODE_UNDEFINED, null, 0, 0); - when(mMockSystemServicesProxy.getTaskThumbnail(anyInt(), anyBoolean())) + when(mMockActivityManagerWrapper.getTaskThumbnail(anyInt(), anyBoolean())) .thenReturn(mThumbnailData); mLoader.setVisible(true); mLoader.setTaskLoadQueueIdle(true); diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/InputConsumerController.java b/packages/SystemUI/src/com/android/systemui/pip/phone/InputConsumerController.java index abc5667251ea..e6d6c5586ad8 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/InputConsumerController.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/InputConsumerController.java @@ -28,8 +28,6 @@ import android.view.InputEvent; import android.view.IWindowManager; import android.view.MotionEvent; -import com.android.systemui.recents.misc.Utilities; - import java.io.PrintWriter; /** diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java index 176112baa2a5..b3244a5947df 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java @@ -305,7 +305,15 @@ public class CustomTile extends QSTileImpl<State> implements TileChangeListener state.state = Tile.STATE_UNAVAILABLE; drawable = mDefaultIcon.loadDrawable(mContext); } - state.icon = new DrawableIcon(drawable); + + final Drawable drawableF = drawable; + state.iconSupplier = () -> { + Drawable.ConstantState cs = drawableF.getConstantState(); + if (cs != null) { + return new DrawableIcon(cs.newDrawable()); + } + return null; + }; state.label = mTile.getLabel(); if (mTile.getContentDescription() != null) { state.contentDescription = mTile.getContentDescription(); diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java index e8c8b9075792..c249e3778c0a 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java @@ -87,14 +87,15 @@ public class QSIconViewImpl extends QSIconView { } protected void updateIcon(ImageView iv, State state) { - if (!Objects.equals(state.icon, iv.getTag(R.id.qs_icon_tag)) + final QSTile.Icon icon = state.iconSupplier != null ? state.iconSupplier.get() : state.icon; + if (!Objects.equals(icon, iv.getTag(R.id.qs_icon_tag)) || !Objects.equals(state.slash, iv.getTag(R.id.qs_slash_tag))) { boolean shouldAnimate = iv.isShown() && mAnimationEnabled && iv.getDrawable() != null; - Drawable d = state.icon != null - ? shouldAnimate ? state.icon.getDrawable(mContext) - : state.icon.getInvisibleDrawable(mContext) : null; - int padding = state.icon != null ? state.icon.getPadding() : 0; + Drawable d = icon != null + ? shouldAnimate ? icon.getDrawable(mContext) + : icon.getInvisibleDrawable(mContext) : null; + int padding = icon != null ? icon.getPadding() : 0; if (d != null) { d.setAutoMirrored(false); d.setLayoutDirection(getLayoutDirection()); @@ -107,7 +108,7 @@ public class QSIconViewImpl extends QSIconView { iv.setImageDrawable(d); } - iv.setTag(R.id.qs_icon_tag, state.icon); + iv.setTag(R.id.qs_icon_tag, icon); iv.setTag(R.id.qs_slash_tag, state.slash); iv.setPadding(0, padding, 0, padding); if (d instanceof Animatable2) { diff --git a/packages/SystemUI/src/com/android/systemui/recents/IRecentsSystemUserCallbacks.aidl b/packages/SystemUI/src/com/android/systemui/recents/IRecentsSystemUserCallbacks.aidl index cc7798e8721b..58d8d8fd600a 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/IRecentsSystemUserCallbacks.aidl +++ b/packages/SystemUI/src/com/android/systemui/recents/IRecentsSystemUserCallbacks.aidl @@ -31,5 +31,6 @@ oneway interface IRecentsSystemUserCallbacks { void sendRecentsDrawnEvent(); void sendDockingTopTaskEvent(int dragMode, in Rect initialRect); void sendLaunchRecentsEvent(); + void sendDockedFirstAnimationFrameEvent(); void setWaitingForTransitionStartEvent(boolean waitingForTransitionStart); } diff --git a/packages/SystemUI/src/com/android/systemui/recents/Recents.java b/packages/SystemUI/src/com/android/systemui/recents/Recents.java index b96bd9b2d7cb..ce1438a14e52 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/Recents.java +++ b/packages/SystemUI/src/com/android/systemui/recents/Recents.java @@ -29,14 +29,13 @@ import android.content.Intent; import android.content.ServiceConnection; import android.content.pm.ActivityInfo; import android.content.res.Configuration; +import android.content.res.Resources; import android.graphics.Point; import android.graphics.Rect; import android.hardware.display.DisplayManager; -import android.os.Build; import android.os.Handler; import android.os.IBinder; import android.os.RemoteException; -import android.os.SystemProperties; import android.os.UserHandle; import android.provider.Settings; import android.util.EventLog; @@ -53,6 +52,7 @@ import com.android.systemui.RecentsComponent; import com.android.systemui.SystemUI; import com.android.systemui.recents.events.EventBus; import com.android.systemui.recents.events.activity.ConfigurationChangedEvent; +import com.android.systemui.recents.events.activity.DockedFirstAnimationFrameEvent; import com.android.systemui.recents.events.activity.DockedTopTaskEvent; import com.android.systemui.recents.events.activity.LaunchTaskFailedEvent; import com.android.systemui.recents.events.activity.RecentsActivityStartingEvent; @@ -62,7 +62,7 @@ import com.android.systemui.recents.events.component.SetWaitingForTransitionStar import com.android.systemui.recents.events.component.ShowUserToastEvent; import com.android.systemui.recents.events.ui.RecentsDrawnEvent; import com.android.systemui.recents.misc.SystemServicesProxy; -import com.android.systemui.recents.model.RecentsTaskLoader; +import com.android.systemui.shared.recents.model.RecentsTaskLoader; import com.android.systemui.stackdivider.Divider; import com.android.systemui.statusbar.CommandQueue; @@ -191,10 +191,20 @@ public class Recents extends SystemUI @Override public void start() { + final Resources res = mContext.getResources(); + final int defaultTaskBarBackgroundColor = + mContext.getColor(R.color.recents_task_bar_default_background_color); + final int defaultTaskViewBackgroundColor = + mContext.getColor(R.color.recents_task_view_default_background_color); sDebugFlags = new RecentsDebugFlags(); sSystemServicesProxy = SystemServicesProxy.getInstance(mContext); sConfiguration = new RecentsConfiguration(mContext); - sTaskLoader = new RecentsTaskLoader(mContext); + sTaskLoader = new RecentsTaskLoader(mContext, + // TODO: Once we start building the AAR, move these into the loader + res.getInteger(R.integer.config_recents_max_thumbnail_count), + res.getInteger(R.integer.config_recents_max_icon_count), + res.getInteger(R.integer.recents_svelte_level)); + sTaskLoader.setDefaultColors(defaultTaskBarBackgroundColor, defaultTaskViewBackgroundColor); mHandler = new Handler(); mImpl = new RecentsImpl(mContext); @@ -596,6 +606,23 @@ public class Recents extends SystemUI } } + public final void onBusEvent(DockedFirstAnimationFrameEvent event) { + SystemServicesProxy ssp = Recents.getSystemServices(); + int processUser = ssp.getProcessUser(); + if (!ssp.isSystemUser(processUser)) { + postToSystemUser(new Runnable() { + @Override + public void run() { + try { + mUserToSystemCallbacks.sendDockedFirstAnimationFrameEvent(); + } catch (RemoteException e) { + Log.e(TAG, "Callback failed", e); + } + } + }); + } + } + /** * Handle screen pinning request. */ diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java index 711885c40692..b75a142864e8 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java @@ -85,11 +85,11 @@ import com.android.systemui.recents.events.ui.focus.FocusPreviousTaskViewEvent; import com.android.systemui.recents.events.ui.focus.NavigateTaskViewEvent; import com.android.systemui.recents.events.ui.focus.NavigateTaskViewEvent.Direction; import com.android.systemui.recents.misc.SystemServicesProxy; -import com.android.systemui.recents.misc.Utilities; -import com.android.systemui.recents.model.RecentsTaskLoadPlan; -import com.android.systemui.recents.model.RecentsTaskLoader; -import com.android.systemui.recents.model.Task; -import com.android.systemui.recents.model.TaskStack; +import com.android.systemui.shared.recents.utilities.Utilities; +import com.android.systemui.shared.recents.model.RecentsTaskLoadPlan; +import com.android.systemui.shared.recents.model.RecentsTaskLoader; +import com.android.systemui.shared.recents.model.Task; +import com.android.systemui.shared.recents.model.TaskStack; import com.android.systemui.recents.views.RecentsView; import com.android.systemui.recents.views.SystemBarScrimViews; import com.android.systemui.statusbar.phone.StatusBar; @@ -427,7 +427,7 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD RecentsTaskLoader loader = Recents.getTaskLoader(); RecentsTaskLoadPlan loadPlan = RecentsImpl.consumeInstanceLoadPlan(); if (loadPlan == null) { - loadPlan = loader.createLoadPlan(this); + loadPlan = new RecentsTaskLoadPlan(this); } // Start loading tasks according to the load plan @@ -441,7 +441,7 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD loadOpts.runningTaskId = launchState.launchedToTaskId; loadOpts.numVisibleTasks = launchState.launchedNumVisibleTasks; loadOpts.numVisibleTaskThumbnails = launchState.launchedNumVisibleThumbnails; - loader.loadTasks(this, loadPlan, loadOpts); + loader.loadTasks(loadPlan, loadOpts); TaskStack stack = loadPlan.getTaskStack(); mRecentsView.onReload(stack, mIsVisible); @@ -815,13 +815,13 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD RecentsConfiguration config = Recents.getConfiguration(); RecentsActivityLaunchState launchState = config.getLaunchState(); RecentsTaskLoader loader = Recents.getTaskLoader(); - RecentsTaskLoadPlan loadPlan = loader.createLoadPlan(this); + RecentsTaskLoadPlan loadPlan = new RecentsTaskLoadPlan(this); loader.preloadTasks(loadPlan, -1 /* runningTaskId */); RecentsTaskLoadPlan.Options loadOpts = new RecentsTaskLoadPlan.Options(); loadOpts.numVisibleTasks = launchState.launchedNumVisibleTasks; loadOpts.numVisibleTaskThumbnails = launchState.launchedNumVisibleThumbnails; - loader.loadTasks(this, loadPlan, loadOpts); + loader.loadTasks(loadPlan, loadOpts); TaskStack stack = loadPlan.getTaskStack(); int numStackTasks = stack.getStackTaskCount(); diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java index 2c3a727e00b9..d2326ce2673d 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java @@ -33,7 +33,6 @@ public class RecentsActivityLaunchState { public boolean launchedFromPipApp; // Set if the next activity that quick-switch will launch is the PiP activity public boolean launchedWithNextPipApp; - public boolean launchedFromBlacklistedApp; public boolean launchedFromHome; public boolean launchedViaDragGesture; public boolean launchedViaDockGesture; @@ -44,7 +43,6 @@ public class RecentsActivityLaunchState { public void reset() { launchedFromHome = false; launchedFromApp = false; - launchedFromBlacklistedApp = false; launchedFromPipApp = false; launchedWithNextPipApp = false; launchedToTaskId = -1; @@ -60,12 +58,6 @@ public class RecentsActivityLaunchState { RecentsDebugFlags debugFlags = Recents.getDebugFlags(); RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState(); if (launchedFromApp) { - if (launchState.launchedFromBlacklistedApp) { - // If we are launching from a blacklisted app, focus the front most task so that the - // next tap will launch the task - return numTasks - 1; - } - if (useGridLayout) { // If coming from another app to the grid layout, focus the front most task return numTasks - 1; diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java index 5dc6f31cae9a..68df1d5bd322 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java @@ -20,31 +20,31 @@ import android.app.ActivityManager; import android.content.Context; import android.content.res.Configuration; import android.content.res.Resources; -import android.graphics.Rect; import android.os.SystemProperties; import com.android.systemui.R; import com.android.systemui.recents.misc.SystemServicesProxy; -import com.android.systemui.recents.model.TaskStack; +import com.android.systemui.recents.views.DockState; +import com.android.systemui.shared.recents.model.TaskStack; /** * Represents the dock regions for each orientation. */ class DockRegion { - public static TaskStack.DockState[] PHONE_LANDSCAPE = { + public static DockState[] PHONE_LANDSCAPE = { // We only allow docking to the left in landscape for now on small devices - TaskStack.DockState.LEFT + DockState.LEFT }; - public static TaskStack.DockState[] PHONE_PORTRAIT = { + public static DockState[] PHONE_PORTRAIT = { // We only allow docking to the top for now on small devices - TaskStack.DockState.TOP + DockState.TOP }; - public static TaskStack.DockState[] TABLET_LANDSCAPE = { - TaskStack.DockState.LEFT, - TaskStack.DockState.RIGHT + public static DockState[] TABLET_LANDSCAPE = { + DockState.LEFT, + DockState.RIGHT }; - public static TaskStack.DockState[] TABLET_PORTRAIT = PHONE_PORTRAIT; + public static DockState[] TABLET_PORTRAIT = PHONE_PORTRAIT; } /** @@ -56,18 +56,6 @@ public class RecentsConfiguration { private static final int LARGE_SCREEN_MIN_DP = 600; private static final int XLARGE_SCREEN_MIN_DP = 720; - /** Levels of svelte in increasing severity/austerity. */ - // No svelting. - public static final int SVELTE_NONE = 0; - // Limit thumbnail cache to number of visible thumbnails when Recents was loaded, disable - // caching thumbnails as you scroll. - public static final int SVELTE_LIMIT_CACHE = 1; - // Disable the thumbnail cache, load thumbnails asynchronously when the activity loads and - // evict all thumbnails when hidden. - public static final int SVELTE_DISABLE_CACHE = 2; - // Disable all thumbnail loading. - public static final int SVELTE_DISABLE_LOADING = 3; - // Launch states public RecentsActivityLaunchState mLaunchState = new RecentsActivityLaunchState(); @@ -125,7 +113,7 @@ public class RecentsConfiguration { * Returns the preferred dock states for the current orientation. * @return a list of dock states for device and its orientation */ - public TaskStack.DockState[] getDockStatesForCurrentOrientation() { + public DockState[] getDockStatesForCurrentOrientation() { boolean isLandscape = mAppContext.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE; RecentsConfiguration config = Recents.getConfiguration(); diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsDebugFlags.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsDebugFlags.java index a8dafbf70f4e..19185939c553 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsDebugFlags.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsDebugFlags.java @@ -21,8 +21,6 @@ public class RecentsDebugFlags { public static class Static { // Enables debug drawing for the transition thumbnail public static final boolean EnableTransitionThumbnailDebugMode = false; - // Enables debug thumbnail to be fetched - public static final boolean EnableThumbnailDebugMode = false; // Disables enter and exit transitions for other tasks for low ram devices public static final boolean DisableRecentsLowRamEnterExitAnimation = false; diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java index 3f8d53f10616..868ed64bab2e 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java @@ -71,12 +71,12 @@ import com.android.systemui.recents.misc.DozeTrigger; import com.android.systemui.recents.misc.ForegroundThread; import com.android.systemui.recents.misc.SystemServicesProxy; import com.android.systemui.recents.misc.TaskStackChangeListener; -import com.android.systemui.recents.model.RecentsTaskLoadPlan; -import com.android.systemui.recents.model.RecentsTaskLoader; -import com.android.systemui.recents.model.Task; -import com.android.systemui.recents.model.Task.TaskKey; -import com.android.systemui.recents.model.TaskStack; -import com.android.systemui.recents.model.ThumbnailData; +import com.android.systemui.shared.recents.model.RecentsTaskLoadPlan; +import com.android.systemui.shared.recents.model.RecentsTaskLoader; +import com.android.systemui.shared.recents.model.Task; +import com.android.systemui.shared.recents.model.Task.TaskKey; +import com.android.systemui.shared.recents.model.TaskStack; +import com.android.systemui.shared.recents.model.ThumbnailData; import com.android.systemui.recents.views.RecentsTransitionHelper; import com.android.systemui.recents.views.RecentsTransitionHelper.AppTransitionAnimationSpecsFuture; import com.android.systemui.recents.views.TaskStackLayoutAlgorithm; @@ -127,7 +127,7 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener // Preloads the next task RecentsConfiguration config = Recents.getConfiguration(); - if (config.svelteLevel == RecentsConfiguration.SVELTE_NONE) { + if (config.svelteLevel == RecentsTaskLoader.SVELTE_NONE) { Rect windowRect = getWindowRect(null /* windowRectOverride */); if (windowRect.isEmpty()) { return; @@ -137,7 +137,7 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener SystemServicesProxy ssp = Recents.getSystemServices(); ActivityManager.RunningTaskInfo runningTaskInfo = ssp.getRunningTask(); RecentsTaskLoader loader = Recents.getTaskLoader(); - RecentsTaskLoadPlan plan = loader.createLoadPlan(mContext); + RecentsTaskLoadPlan plan = new RecentsTaskLoadPlan(mContext); loader.preloadTasks(plan, -1); TaskStack stack = plan.getTaskStack(); RecentsActivityLaunchState launchState = new RecentsActivityLaunchState(); @@ -164,7 +164,7 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener launchOpts.onlyLoadPausedActivities = true; launchOpts.loadThumbnails = true; } - loader.loadTasks(mContext, plan, launchOpts); + loader.loadTasks(plan, launchOpts); } } @@ -203,7 +203,7 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener } EventBus.getDefault().send(new TaskSnapshotChangedEvent(taskId, - ThumbnailData.createFromTaskSnapshot(snapshot))); + new ThumbnailData(snapshot))); } } @@ -278,13 +278,13 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener // When we start, preload the data associated with the previous recent tasks. // We can use a new plan since the caches will be the same. RecentsTaskLoader loader = Recents.getTaskLoader(); - RecentsTaskLoadPlan plan = loader.createLoadPlan(mContext); + RecentsTaskLoadPlan plan = new RecentsTaskLoadPlan(mContext); loader.preloadTasks(plan, -1); RecentsTaskLoadPlan.Options launchOpts = new RecentsTaskLoadPlan.Options(); launchOpts.numVisibleTasks = loader.getIconCacheSize(); launchOpts.numVisibleTaskThumbnails = loader.getThumbnailCacheSize(); launchOpts.onlyLoadForCache = true; - loader.loadTasks(mContext, plan, launchOpts); + loader.loadTasks(plan, launchOpts); } public void onConfigurationChanged() { @@ -471,7 +471,7 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener } RecentsTaskLoader loader = Recents.getTaskLoader(); - sInstanceLoadPlan = loader.createLoadPlan(mContext); + sInstanceLoadPlan = new RecentsTaskLoadPlan(mContext); loader.preloadTasks(sInstanceLoadPlan, runningTask.id); TaskStack stack = sInstanceLoadPlan.getTaskStack(); if (stack.getTaskCount() > 0) { @@ -511,7 +511,7 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener public void showNextTask() { SystemServicesProxy ssp = Recents.getSystemServices(); RecentsTaskLoader loader = Recents.getTaskLoader(); - RecentsTaskLoadPlan plan = loader.createLoadPlan(mContext); + RecentsTaskLoadPlan plan = new RecentsTaskLoadPlan(mContext); loader.preloadTasks(plan, -1); TaskStack focusedStack = plan.getTaskStack(); @@ -566,7 +566,7 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener public void showRelativeAffiliatedTask(boolean showNextTask) { SystemServicesProxy ssp = Recents.getSystemServices(); RecentsTaskLoader loader = Recents.getTaskLoader(); - RecentsTaskLoadPlan plan = loader.createLoadPlan(mContext); + RecentsTaskLoadPlan plan = new RecentsTaskLoadPlan(mContext); loader.preloadTasks(plan, -1); TaskStack focusedStack = plan.getTaskStack(); @@ -827,7 +827,7 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener launchOpts.runningTaskId = runningTaskId; launchOpts.loadThumbnails = false; launchOpts.onlyLoadForCache = true; - Recents.getTaskLoader().loadTasks(mContext, sInstanceLoadPlan, launchOpts); + Recents.getTaskLoader().loadTasks(sInstanceLoadPlan, launchOpts); } /** @@ -947,12 +947,8 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener boolean isHomeStackVisible, boolean animate, int growTarget) { RecentsTaskLoader loader = Recents.getTaskLoader(); RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState(); - SystemServicesProxy ssp = Recents.getSystemServices(); - boolean isBlacklisted = (runningTask != null) - ? ssp.isBlackListedActivity(runningTask.baseActivity.getClassName()) - : false; - int runningTaskId = !mLaunchedWhileDocking && !isBlacklisted && (runningTask != null) + int runningTaskId = !mLaunchedWhileDocking && (runningTask != null) ? runningTask.id : -1; @@ -961,7 +957,7 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener // the stacks might have changed. if (mLaunchedWhileDocking || mTriggeredFromAltTab || sInstanceLoadPlan == null) { // Create a new load plan if preloadRecents() was never triggered - sInstanceLoadPlan = loader.createLoadPlan(mContext); + sInstanceLoadPlan = new RecentsTaskLoadPlan(mContext); } if (mLaunchedWhileDocking || mTriggeredFromAltTab || !sInstanceLoadPlan.hasTasks()) { loader.preloadTasks(sInstanceLoadPlan, runningTaskId); @@ -975,7 +971,6 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener // Update the launch state that we need in updateHeaderBarLayout() launchState.launchedFromHome = !useThumbnailTransition && !mLaunchedWhileDocking; launchState.launchedFromApp = useThumbnailTransition || mLaunchedWhileDocking; - launchState.launchedFromBlacklistedApp = launchState.launchedFromApp && isBlacklisted; launchState.launchedFromPipApp = false; launchState.launchedWithNextPipApp = stack.isNextLaunchTargetPip(RecentsImpl.getLastPipTime()); @@ -1011,9 +1006,7 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener } Pair<ActivityOptions, AppTransitionAnimationSpecsFuture> pair; - if (isBlacklisted) { - pair = new Pair<>(getUnknownTransitionActivityOptions(), null); - } else if (useThumbnailTransition) { + if (useThumbnailTransition) { // Try starting with a thumbnail transition pair = getThumbnailTransitionActivityOptions(runningTask, windowOverrideRect); } else { diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsSystemUser.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsSystemUser.java index 1285626015d2..ff1f7dc5a2a8 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsSystemUser.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsSystemUser.java @@ -27,6 +27,7 @@ import android.util.SparseArray; import com.android.systemui.EventLogConstants; import com.android.systemui.EventLogTags; import com.android.systemui.recents.events.EventBus; +import com.android.systemui.recents.events.activity.DockedFirstAnimationFrameEvent; import com.android.systemui.recents.events.activity.DockedTopTaskEvent; import com.android.systemui.recents.events.activity.RecentsActivityStartingEvent; import com.android.systemui.recents.events.component.SetWaitingForTransitionStartEvent; @@ -108,6 +109,11 @@ public class RecentsSystemUser extends IRecentsSystemUserCallbacks.Stub { } @Override + public void sendDockedFirstAnimationFrameEvent() throws RemoteException { + EventBus.getDefault().post(new DockedFirstAnimationFrameEvent()); + } + + @Override public void setWaitingForTransitionStartEvent(boolean waitingForTransitionStart) { EventBus.getDefault().post(new SetWaitingForTransitionStartEvent( waitingForTransitionStart)); diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/CancelEnterRecentsWindowAnimationEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/activity/CancelEnterRecentsWindowAnimationEvent.java index 7604de1d05d0..fec34e3cd23d 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/events/activity/CancelEnterRecentsWindowAnimationEvent.java +++ b/packages/SystemUI/src/com/android/systemui/recents/events/activity/CancelEnterRecentsWindowAnimationEvent.java @@ -17,7 +17,7 @@ package com.android.systemui.recents.events.activity; import com.android.systemui.recents.events.EventBus; -import com.android.systemui.recents.model.Task; +import com.android.systemui.shared.recents.model.Task; /** * This is sent when we want to cancel the enter-recents window animation for the launch task. diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/LaunchTaskEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/activity/LaunchTaskEvent.java index 862a1eee8c39..2409f39d3760 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/events/activity/LaunchTaskEvent.java +++ b/packages/SystemUI/src/com/android/systemui/recents/events/activity/LaunchTaskEvent.java @@ -22,7 +22,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import android.graphics.Rect; import com.android.systemui.recents.events.EventBus; -import com.android.systemui.recents.model.Task; +import com.android.systemui.shared.recents.model.Task; import com.android.systemui.recents.views.TaskView; /** diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/MultiWindowStateChangedEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/activity/MultiWindowStateChangedEvent.java index 64eeafa1ae17..e4972b1fd7f4 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/events/activity/MultiWindowStateChangedEvent.java +++ b/packages/SystemUI/src/com/android/systemui/recents/events/activity/MultiWindowStateChangedEvent.java @@ -17,7 +17,7 @@ package com.android.systemui.recents.events.activity; import com.android.systemui.recents.events.EventBus; -import com.android.systemui.recents.model.TaskStack; +import com.android.systemui.shared.recents.model.TaskStack; /** * This is sent by the activity whenever the multi-window state has changed. diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/TaskStackUpdatedEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/activity/TaskStackUpdatedEvent.java index 0d614e8c675c..51d02b5b0018 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/events/activity/TaskStackUpdatedEvent.java +++ b/packages/SystemUI/src/com/android/systemui/recents/events/activity/TaskStackUpdatedEvent.java @@ -17,7 +17,7 @@ package com.android.systemui.recents.events.activity; import com.android.systemui.recents.events.EventBus; -import com.android.systemui.recents.model.TaskStack; +import com.android.systemui.shared.recents.model.TaskStack; /** * This is sent by the activity whenever the task stach has changed. diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/ui/DeleteTaskDataEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/ui/DeleteTaskDataEvent.java index 4ed027084def..b52e83b81649 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/events/ui/DeleteTaskDataEvent.java +++ b/packages/SystemUI/src/com/android/systemui/recents/events/ui/DeleteTaskDataEvent.java @@ -17,7 +17,7 @@ package com.android.systemui.recents.events.ui; import com.android.systemui.recents.events.EventBus; -import com.android.systemui.recents.model.Task; +import com.android.systemui.shared.recents.model.Task; /** * This is sent when the data associated with a given {@link Task} should be deleted from the diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/ui/ShowApplicationInfoEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/ui/ShowApplicationInfoEvent.java index 40c30b884eae..da19384ae93a 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/events/ui/ShowApplicationInfoEvent.java +++ b/packages/SystemUI/src/com/android/systemui/recents/events/ui/ShowApplicationInfoEvent.java @@ -17,7 +17,7 @@ package com.android.systemui.recents.events.ui; import com.android.systemui.recents.events.EventBus; -import com.android.systemui.recents.model.Task; +import com.android.systemui.shared.recents.model.Task; /** * This is sent when a user wants to show the application info for a {@link Task}. diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/ui/TaskSnapshotChangedEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/ui/TaskSnapshotChangedEvent.java index e0ed7a9e7e35..f08292801b62 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/events/ui/TaskSnapshotChangedEvent.java +++ b/packages/SystemUI/src/com/android/systemui/recents/events/ui/TaskSnapshotChangedEvent.java @@ -17,7 +17,7 @@ package com.android.systemui.recents.events.ui; import com.android.systemui.recents.events.EventBus; -import com.android.systemui.recents.model.ThumbnailData; +import com.android.systemui.shared.recents.model.ThumbnailData; /** * Sent when a task snapshot has changed. diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/ui/TaskViewDismissedEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/ui/TaskViewDismissedEvent.java index 0628c5015153..881a64af5b0f 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/events/ui/TaskViewDismissedEvent.java +++ b/packages/SystemUI/src/com/android/systemui/recents/events/ui/TaskViewDismissedEvent.java @@ -17,8 +17,8 @@ package com.android.systemui.recents.events.ui; import com.android.systemui.recents.events.EventBus; -import com.android.systemui.recents.model.Task; -import com.android.systemui.recents.views.AnimationProps; +import com.android.systemui.shared.recents.model.Task; +import com.android.systemui.shared.recents.utilities.AnimationProps; import com.android.systemui.recents.views.TaskView; /** diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragDropTargetChangedEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragDropTargetChangedEvent.java index 216be6121f8d..cf61b1ef7637 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragDropTargetChangedEvent.java +++ b/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragDropTargetChangedEvent.java @@ -17,7 +17,7 @@ package com.android.systemui.recents.events.ui.dragndrop; import com.android.systemui.recents.events.EventBus; -import com.android.systemui.recents.model.Task; +import com.android.systemui.shared.recents.model.Task; import com.android.systemui.recents.views.DropTarget; /** diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragEndCancelledEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragEndCancelledEvent.java index edd799597ea6..297afc53c557 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragEndCancelledEvent.java +++ b/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragEndCancelledEvent.java @@ -17,9 +17,8 @@ package com.android.systemui.recents.events.ui.dragndrop; import com.android.systemui.recents.events.EventBus; -import com.android.systemui.recents.model.Task; -import com.android.systemui.recents.model.TaskStack; -import com.android.systemui.recents.views.DropTarget; +import com.android.systemui.shared.recents.model.Task; +import com.android.systemui.shared.recents.model.TaskStack; import com.android.systemui.recents.views.TaskView; /** diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragEndEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragEndEvent.java index 73c282fe8816..73cbde998319 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragEndEvent.java +++ b/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragEndEvent.java @@ -17,7 +17,7 @@ package com.android.systemui.recents.events.ui.dragndrop; import com.android.systemui.recents.events.EventBus; -import com.android.systemui.recents.model.Task; +import com.android.systemui.shared.recents.model.Task; import com.android.systemui.recents.views.DropTarget; import com.android.systemui.recents.views.TaskView; diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragStartEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragStartEvent.java index e57fa2d86a66..021be77bcc8b 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragStartEvent.java +++ b/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragStartEvent.java @@ -19,7 +19,7 @@ package com.android.systemui.recents.events.ui.dragndrop; import android.graphics.Point; import com.android.systemui.recents.events.EventBus; -import com.android.systemui.recents.model.Task; +import com.android.systemui.shared.recents.model.Task; import com.android.systemui.recents.views.TaskView; /** diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragStartInitializeDropTargetsEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragStartInitializeDropTargetsEvent.java index 7030729d2a04..64ba5748bb89 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragStartInitializeDropTargetsEvent.java +++ b/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragStartInitializeDropTargetsEvent.java @@ -17,7 +17,7 @@ package com.android.systemui.recents.events.ui.dragndrop; import com.android.systemui.recents.events.EventBus; -import com.android.systemui.recents.model.Task; +import com.android.systemui.shared.recents.model.Task; import com.android.systemui.recents.views.RecentsViewTouchHandler; import com.android.systemui.recents.views.TaskView; diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java index a392effb1bca..87f24fdb6cdb 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java +++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java @@ -16,7 +16,6 @@ package com.android.systemui.recents.misc; -import static android.app.ActivityManager.RECENT_IGNORE_UNAVAILABLE; import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; @@ -27,21 +26,17 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMAR import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; -import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityManager; import android.app.ActivityManager.StackInfo; import android.app.ActivityOptions; import android.app.AppGlobals; import android.app.IActivityManager; -import android.app.KeyguardManager; import android.app.WindowConfiguration; import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; -import android.content.pm.ActivityInfo; -import android.content.pm.ApplicationInfo; import android.content.pm.IPackageManager; import android.content.pm.PackageManager; import android.content.res.Resources; @@ -53,7 +48,6 @@ import android.graphics.Point; import android.graphics.PorterDuff; import android.graphics.PorterDuffXfermode; import android.graphics.Rect; -import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.os.Handler; import android.os.IRemoteCallback; @@ -66,8 +60,6 @@ import android.os.UserManager; import android.provider.Settings; import android.service.dreams.DreamService; import android.service.dreams.IDreamManager; -import android.util.ArraySet; -import android.util.IconDrawableFactory; import android.util.Log; import android.util.MutableBoolean; import android.view.Display; @@ -84,17 +76,11 @@ import com.android.internal.os.BackgroundThread; import com.android.systemui.Dependency; import com.android.systemui.R; import com.android.systemui.UiOffloadThread; -import com.android.systemui.pip.tv.PipMenuActivity; import com.android.systemui.recents.Recents; -import com.android.systemui.recents.RecentsDebugFlags.Static; import com.android.systemui.recents.RecentsImpl; -import com.android.systemui.recents.model.Task; -import com.android.systemui.recents.model.ThumbnailData; +import com.android.systemui.shared.recents.model.Task; import com.android.systemui.statusbar.policy.UserInfoController; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Iterator; import java.util.List; /** @@ -111,26 +97,18 @@ public class SystemServicesProxy { sBitmapOptions.inPreferredConfig = Bitmap.Config.RGB_565; } - final static List<String> sRecentsBlacklist; - static { - sRecentsBlacklist = new ArrayList<>(); - sRecentsBlacklist.add(PipMenuActivity.class.getName()); - } - private static SystemServicesProxy sSystemServicesProxy; AccessibilityManager mAccm; ActivityManager mAm; IActivityManager mIam; PackageManager mPm; - IconDrawableFactory mDrawableFactory; IPackageManager mIpm; private final IDreamManager mDreamManager; private final Context mContext; AssistUtils mAssistUtils; WindowManager mWm; IWindowManager mIwm; - KeyguardManager mKgm; UserManager mUm; Display mDisplay; String mRecentsPackage; @@ -166,12 +144,10 @@ public class SystemServicesProxy { mAm = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); mIam = ActivityManager.getService(); mPm = context.getPackageManager(); - mDrawableFactory = IconDrawableFactory.newInstance(context); mIpm = AppGlobals.getPackageManager(); mAssistUtils = new AssistUtils(context); mWm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); mIwm = WindowManagerGlobal.getWindowManagerService(); - mKgm = (KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE); mUm = UserManager.get(context); mDreamManager = IDreamManager.Stub.asInterface( ServiceManager.checkService(DreamService.DREAM_SERVICE)); @@ -199,9 +175,6 @@ public class SystemServicesProxy { // calls to fetch it. UserInfoController userInfoController = Dependency.get(UserInfoController.class); userInfoController.addCallback(mOnUserInfoChangedListener); - - Collections.addAll(sRecentsBlacklist, - res.getStringArray(R.array.recents_blacklist_array)); } /** @@ -223,39 +196,6 @@ public class SystemServicesProxy { } /** - * @return whether the provided {@param className} is blacklisted - */ - public boolean isBlackListedActivity(String className) { - return sRecentsBlacklist.contains(className); - } - - /** - * Returns a list of the recents tasks. - */ - public List<ActivityManager.RecentTaskInfo> getRecentTasks(int numTasks, int userId) { - if (mAm == null) return null; - - try { - List<ActivityManager.RecentTaskInfo> tasks = mIam.getRecentTasks(numTasks, - RECENT_IGNORE_UNAVAILABLE, userId).getList(); - Iterator<ActivityManager.RecentTaskInfo> iter = tasks.iterator(); - while (iter.hasNext()) { - ActivityManager.RecentTaskInfo t = iter.next(); - - // Remove the task if it or it's package are blacklsited - if (sRecentsBlacklist.contains(t.realActivity.getClassName()) || - sRecentsBlacklist.contains(t.realActivity.getPackageName())) { - iter.remove(); - } - } - return tasks; - } catch (Exception e) { - Log.e(TAG, "Failed to get recent tasks", e); - return new ArrayList<>(); - } - } - - /** * Returns the top running task. */ public ActivityManager.RunningTaskInfo getRunningTask() { @@ -457,43 +397,6 @@ public class SystemServicesProxy { } } - /** Returns the top task thumbnail for the given task id */ - public ThumbnailData getTaskThumbnail(int taskId, boolean reduced) { - if (mAm == null) return null; - - // If we are mocking, then just return a dummy thumbnail - if (Static.EnableTransitionThumbnailDebugMode) { - ThumbnailData thumbnailData = new ThumbnailData(); - thumbnailData.thumbnail = Bitmap.createBitmap(mDummyThumbnailWidth, - mDummyThumbnailHeight, Bitmap.Config.ARGB_8888); - thumbnailData.thumbnail.eraseColor(0xff333333); - return thumbnailData; - } - - return getThumbnail(taskId, reduced); - } - - /** - * Returns a task thumbnail from the activity manager - */ - public @NonNull ThumbnailData getThumbnail(int taskId, boolean reducedResolution) { - if (mAm == null) { - return new ThumbnailData(); - } - - ActivityManager.TaskSnapshot snapshot = null; - try { - snapshot = mIam.getTaskSnapshot(taskId, reducedResolution); - } catch (RemoteException e) { - Log.w(TAG, "Failed to retrieve snapshot", e); - } - if (snapshot != null) { - return ThumbnailData.createFromTaskSnapshot(snapshot); - } else { - return new ThumbnailData(); - } - } - /** Set the task's windowing mode. */ public void setTaskWindowingMode(int taskId, int windowingMode) { if (mIam == null) return; @@ -531,112 +434,6 @@ public class SystemServicesProxy { }); } - /** - * Returns the activity info for a given component name. - * - * @param cn The component name of the activity. - * @param userId The userId of the user that this is for. - */ - public ActivityInfo getActivityInfo(ComponentName cn, int userId) { - if (mIpm == null) return null; - - try { - return mIpm.getActivityInfo(cn, PackageManager.GET_META_DATA, userId); - } catch (RemoteException e) { - e.printStackTrace(); - return null; - } - } - - /** - * Returns the activity info for a given component name. - * - * @param cn The component name of the activity. - */ - public ActivityInfo getActivityInfo(ComponentName cn) { - if (mPm == null) return null; - - try { - return mPm.getActivityInfo(cn, PackageManager.GET_META_DATA); - } catch (PackageManager.NameNotFoundException e) { - e.printStackTrace(); - return null; - } - } - - /** - * Returns the activity label, badging if necessary. - */ - public String getBadgedActivityLabel(ActivityInfo info, int userId) { - if (mPm == null) return null; - - return getBadgedLabel(info.loadLabel(mPm).toString(), userId); - } - - /** - * Returns the application label, badging if necessary. - */ - public String getBadgedApplicationLabel(ApplicationInfo appInfo, int userId) { - if (mPm == null) return null; - - return getBadgedLabel(appInfo.loadLabel(mPm).toString(), userId); - } - - /** - * Returns the content description for a given task, badging it if necessary. The content - * description joins the app and activity labels. - */ - public String getBadgedContentDescription(ActivityInfo info, int userId, - ActivityManager.TaskDescription td, Resources res) { - String activityLabel; - if (td != null && td.getLabel() != null) { - activityLabel = td.getLabel(); - } else { - activityLabel = info.loadLabel(mPm).toString(); - } - String applicationLabel = info.applicationInfo.loadLabel(mPm).toString(); - String badgedApplicationLabel = getBadgedLabel(applicationLabel, userId); - return applicationLabel.equals(activityLabel) ? badgedApplicationLabel - : res.getString(R.string.accessibility_recents_task_header, - badgedApplicationLabel, activityLabel); - } - - /** - * Returns the activity icon for the ActivityInfo for a user, badging if - * necessary. - */ - public Drawable getBadgedActivityIcon(ActivityInfo info, int userId) { - if (mPm == null) return null; - - return mDrawableFactory.getBadgedIcon(info, info.applicationInfo, userId); - } - - /** - * Returns the application icon for the ApplicationInfo for a user, badging if - * necessary. - */ - public Drawable getBadgedApplicationIcon(ApplicationInfo appInfo, int userId) { - if (mPm == null) return null; - - return mDrawableFactory.getBadgedIcon(appInfo, userId); - } - - /** - * Returns the task description icon, loading and badging it if it necessary. - */ - public Drawable getBadgedTaskDescriptionIcon(ActivityManager.TaskDescription taskDescription, - int userId, Resources res) { - Bitmap tdIcon = taskDescription.getInMemoryIcon(); - if (tdIcon == null) { - tdIcon = ActivityManager.TaskDescription.loadTaskDescriptionIcon( - taskDescription.getIconFilename(), userId); - } - if (tdIcon != null) { - return getBadgedIcon(new BitmapDrawable(res, tdIcon), userId); - } - return null; - } - public ActivityManager.TaskDescription getTaskDescription(int taskId) { try { return mIam.getTaskDescription(taskId); @@ -646,46 +443,6 @@ public class SystemServicesProxy { } /** - * Returns the given icon for a user, badging if necessary. - */ - private Drawable getBadgedIcon(Drawable icon, int userId) { - if (userId != UserHandle.myUserId()) { - icon = mPm.getUserBadgedIcon(icon, new UserHandle(userId)); - } - return icon; - } - - /** - * Returns a banner used on TV for the specified Activity. - */ - public Drawable getActivityBanner(ActivityInfo info) { - if (mPm == null) return null; - - Drawable banner = info.loadBanner(mPm); - return banner; - } - - /** - * Returns the given label for a user, badging if necessary. - */ - private String getBadgedLabel(String label, int userId) { - if (userId != UserHandle.myUserId()) { - label = mPm.getUserBadgedLabel(label, new UserHandle(userId)).toString(); - } - return label; - } - - /** - * Returns whether the provided {@param userId} is currently locked (and showing Keyguard). - */ - public boolean isDeviceLocked(int userId) { - if (mKgm == null) { - return false; - } - return mKgm.isDeviceLocked(userId); - } - - /** * Returns whether the provided {@param userId} represents the system user. */ public boolean isSystemUser(int userId) { @@ -790,7 +547,7 @@ public class SystemServicesProxy { ActivityManager.StackInfo stackInfo = mIam.getStackInfo(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_RECENTS); if (stackInfo == null) { - stackInfo = mIam.getStackInfo(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD); + stackInfo = mIam.getStackInfo(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD); } if (stackInfo != null) { windowRect.set(stackInfo.bounds); diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java deleted file mode 100644 index 32e62cf29a4e..000000000000 --- a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java +++ /dev/null @@ -1,856 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.recents.model; - -import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT; -import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT; -import static android.view.WindowManager.DOCKED_BOTTOM; -import static android.view.WindowManager.DOCKED_INVALID; -import static android.view.WindowManager.DOCKED_LEFT; -import static android.view.WindowManager.DOCKED_RIGHT; -import static android.view.WindowManager.DOCKED_TOP; - -import android.animation.Animator; -import android.animation.AnimatorSet; -import android.animation.ObjectAnimator; -import android.animation.PropertyValuesHolder; -import android.annotation.IntDef; -import android.content.ComponentName; -import android.content.Context; -import android.content.res.Configuration; -import android.content.res.Resources; -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.Paint; -import android.graphics.Point; -import android.graphics.Rect; -import android.graphics.RectF; -import android.graphics.drawable.ColorDrawable; -import android.util.ArrayMap; -import android.util.ArraySet; -import android.util.IntProperty; -import android.util.SparseArray; -import android.view.animation.Interpolator; - -import com.android.internal.policy.DockedDividerUtils; -import com.android.systemui.Interpolators; -import com.android.systemui.R; -import com.android.systemui.recents.Recents; -import com.android.systemui.recents.misc.SystemServicesProxy; -import com.android.systemui.recents.misc.Utilities; -import com.android.systemui.recents.views.AnimationProps; -import com.android.systemui.recents.views.DropTarget; -import com.android.systemui.recents.views.TaskStackLayoutAlgorithm; - -import java.io.PrintWriter; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.util.ArrayList; -import java.util.List; - - -/** - * An interface for a task filter to query whether a particular task should show in a stack. - */ -interface TaskFilter { - /** Returns whether the filter accepts the specified task */ - boolean acceptTask(SparseArray<Task> taskIdMap, Task t, int index); -} - -/** - * A list of filtered tasks. - */ -class FilteredTaskList { - - ArrayList<Task> mTasks = new ArrayList<>(); - ArrayList<Task> mFilteredTasks = new ArrayList<>(); - ArrayMap<Task.TaskKey, Integer> mFilteredTaskIndices = new ArrayMap<>(); - TaskFilter mFilter; - - /** Sets the task filter, saving the current touch state */ - boolean setFilter(TaskFilter filter) { - ArrayList<Task> prevFilteredTasks = new ArrayList<>(mFilteredTasks); - mFilter = filter; - updateFilteredTasks(); - if (!prevFilteredTasks.equals(mFilteredTasks)) { - return true; - } else { - return false; - } - } - - /** Removes the task filter and returns the previous touch state */ - void removeFilter() { - mFilter = null; - updateFilteredTasks(); - } - - /** Adds a new task to the task list */ - void add(Task t) { - mTasks.add(t); - updateFilteredTasks(); - } - - /** Sets the list of tasks */ - void set(List<Task> tasks) { - mTasks.clear(); - mTasks.addAll(tasks); - updateFilteredTasks(); - } - - /** Removes a task from the base list only if it is in the filtered list */ - boolean remove(Task t) { - if (mFilteredTasks.contains(t)) { - boolean removed = mTasks.remove(t); - updateFilteredTasks(); - return removed; - } - return false; - } - - /** Returns the index of this task in the list of filtered tasks */ - int indexOf(Task t) { - if (t != null && mFilteredTaskIndices.containsKey(t.key)) { - return mFilteredTaskIndices.get(t.key); - } - return -1; - } - - /** Returns the size of the list of filtered tasks */ - int size() { - return mFilteredTasks.size(); - } - - /** Returns whether the filtered list contains this task */ - boolean contains(Task t) { - return mFilteredTaskIndices.containsKey(t.key); - } - - /** Updates the list of filtered tasks whenever the base task list changes */ - private void updateFilteredTasks() { - mFilteredTasks.clear(); - if (mFilter != null) { - // Create a sparse array from task id to Task - SparseArray<Task> taskIdMap = new SparseArray<>(); - int taskCount = mTasks.size(); - for (int i = 0; i < taskCount; i++) { - Task t = mTasks.get(i); - taskIdMap.put(t.key.id, t); - } - - for (int i = 0; i < taskCount; i++) { - Task t = mTasks.get(i); - if (mFilter.acceptTask(taskIdMap, t, i)) { - mFilteredTasks.add(t); - } - } - } else { - mFilteredTasks.addAll(mTasks); - } - updateFilteredTaskIndices(); - } - - /** Updates the mapping of tasks to indices. */ - private void updateFilteredTaskIndices() { - int taskCount = mFilteredTasks.size(); - mFilteredTaskIndices.clear(); - for (int i = 0; i < taskCount; i++) { - Task t = mFilteredTasks.get(i); - mFilteredTaskIndices.put(t.key, i); - } - } - - /** Returns the list of filtered tasks */ - ArrayList<Task> getTasks() { - return mFilteredTasks; - } -} - -/** - * The task stack contains a list of multiple tasks. - */ -public class TaskStack { - - private static final String TAG = "TaskStack"; - - /** Task stack callbacks */ - public interface TaskStackCallbacks { - /** - * Notifies when a new task has been added to the stack. - */ - void onStackTaskAdded(TaskStack stack, Task newTask); - - /** - * Notifies when a task has been removed from the stack. - */ - void onStackTaskRemoved(TaskStack stack, Task removedTask, Task newFrontMostTask, - AnimationProps animation, boolean fromDockGesture, - boolean dismissRecentsIfAllRemoved); - - /** - * Notifies when all tasks have been removed from the stack. - */ - void onStackTasksRemoved(TaskStack stack); - - /** - * Notifies when tasks in the stack have been updated. - */ - void onStackTasksUpdated(TaskStack stack); - } - - /** - * The various possible dock states when dragging and dropping a task. - */ - public static class DockState implements DropTarget { - - public static final int DOCK_AREA_BG_COLOR = 0xFFffffff; - public static final int DOCK_AREA_GRID_BG_COLOR = 0xFF000000; - - // The rotation to apply to the hint text - @Retention(RetentionPolicy.SOURCE) - @IntDef({HORIZONTAL, VERTICAL}) - public @interface TextOrientation {} - private static final int HORIZONTAL = 0; - private static final int VERTICAL = 1; - - private static final int DOCK_AREA_ALPHA = 80; - public static final DockState NONE = new DockState(DOCKED_INVALID, -1, 80, 255, HORIZONTAL, - null, null, null); - public static final DockState LEFT = new DockState(DOCKED_LEFT, - DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT, DOCK_AREA_ALPHA, 0, VERTICAL, - new RectF(0, 0, 0.125f, 1), new RectF(0, 0, 0.125f, 1), - new RectF(0, 0, 0.5f, 1)); - public static final DockState TOP = new DockState(DOCKED_TOP, - DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT, DOCK_AREA_ALPHA, 0, HORIZONTAL, - new RectF(0, 0, 1, 0.125f), new RectF(0, 0, 1, 0.125f), - new RectF(0, 0, 1, 0.5f)); - public static final DockState RIGHT = new DockState(DOCKED_RIGHT, - DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT, DOCK_AREA_ALPHA, 0, VERTICAL, - new RectF(0.875f, 0, 1, 1), new RectF(0.875f, 0, 1, 1), - new RectF(0.5f, 0, 1, 1)); - public static final DockState BOTTOM = new DockState(DOCKED_BOTTOM, - DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT, DOCK_AREA_ALPHA, 0, HORIZONTAL, - new RectF(0, 0.875f, 1, 1), new RectF(0, 0.875f, 1, 1), - new RectF(0, 0.5f, 1, 1)); - - @Override - public boolean acceptsDrop(int x, int y, int width, int height, Rect insets, - boolean isCurrentTarget) { - if (isCurrentTarget) { - getMappedRect(expandedTouchDockArea, width, height, mTmpRect); - return mTmpRect.contains(x, y); - } else { - getMappedRect(touchArea, width, height, mTmpRect); - updateBoundsWithSystemInsets(mTmpRect, insets); - return mTmpRect.contains(x, y); - } - } - - // Represents the view state of this dock state - public static class ViewState { - private static final IntProperty<ViewState> HINT_ALPHA = - new IntProperty<ViewState>("drawableAlpha") { - @Override - public void setValue(ViewState object, int alpha) { - object.mHintTextAlpha = alpha; - object.dockAreaOverlay.invalidateSelf(); - } - - @Override - public Integer get(ViewState object) { - return object.mHintTextAlpha; - } - }; - - public final int dockAreaAlpha; - public final ColorDrawable dockAreaOverlay; - public final int hintTextAlpha; - public final int hintTextOrientation; - - private final int mHintTextResId; - private String mHintText; - private Paint mHintTextPaint; - private Point mHintTextBounds = new Point(); - private int mHintTextAlpha = 255; - private AnimatorSet mDockAreaOverlayAnimator; - private Rect mTmpRect = new Rect(); - - private ViewState(int areaAlpha, int hintAlpha, @TextOrientation int hintOrientation, - int hintTextResId) { - dockAreaAlpha = areaAlpha; - dockAreaOverlay = new ColorDrawable(Recents.getConfiguration().isGridEnabled - ? DOCK_AREA_GRID_BG_COLOR : DOCK_AREA_BG_COLOR); - dockAreaOverlay.setAlpha(0); - hintTextAlpha = hintAlpha; - hintTextOrientation = hintOrientation; - mHintTextResId = hintTextResId; - mHintTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG); - mHintTextPaint.setColor(Color.WHITE); - } - - /** - * Updates the view state with the given context. - */ - public void update(Context context) { - Resources res = context.getResources(); - mHintText = context.getString(mHintTextResId); - mHintTextPaint.setTextSize(res.getDimensionPixelSize( - R.dimen.recents_drag_hint_text_size)); - mHintTextPaint.getTextBounds(mHintText, 0, mHintText.length(), mTmpRect); - mHintTextBounds.set((int) mHintTextPaint.measureText(mHintText), mTmpRect.height()); - } - - /** - * Draws the current view state. - */ - public void draw(Canvas canvas) { - // Draw the overlay background - if (dockAreaOverlay.getAlpha() > 0) { - dockAreaOverlay.draw(canvas); - } - - // Draw the hint text - if (mHintTextAlpha > 0) { - Rect bounds = dockAreaOverlay.getBounds(); - int x = bounds.left + (bounds.width() - mHintTextBounds.x) / 2; - int y = bounds.top + (bounds.height() + mHintTextBounds.y) / 2; - mHintTextPaint.setAlpha(mHintTextAlpha); - if (hintTextOrientation == VERTICAL) { - canvas.save(); - canvas.rotate(-90f, bounds.centerX(), bounds.centerY()); - } - canvas.drawText(mHintText, x, y, mHintTextPaint); - if (hintTextOrientation == VERTICAL) { - canvas.restore(); - } - } - } - - /** - * Creates a new bounds and alpha animation. - */ - public void startAnimation(Rect bounds, int areaAlpha, int hintAlpha, int duration, - Interpolator interpolator, boolean animateAlpha, boolean animateBounds) { - if (mDockAreaOverlayAnimator != null) { - mDockAreaOverlayAnimator.cancel(); - } - - ObjectAnimator anim; - ArrayList<Animator> animators = new ArrayList<>(); - if (dockAreaOverlay.getAlpha() != areaAlpha) { - if (animateAlpha) { - anim = ObjectAnimator.ofInt(dockAreaOverlay, - Utilities.DRAWABLE_ALPHA, dockAreaOverlay.getAlpha(), areaAlpha); - anim.setDuration(duration); - anim.setInterpolator(interpolator); - animators.add(anim); - } else { - dockAreaOverlay.setAlpha(areaAlpha); - } - } - if (mHintTextAlpha != hintAlpha) { - if (animateAlpha) { - anim = ObjectAnimator.ofInt(this, HINT_ALPHA, mHintTextAlpha, - hintAlpha); - anim.setDuration(150); - anim.setInterpolator(hintAlpha > mHintTextAlpha - ? Interpolators.ALPHA_IN - : Interpolators.ALPHA_OUT); - animators.add(anim); - } else { - mHintTextAlpha = hintAlpha; - dockAreaOverlay.invalidateSelf(); - } - } - if (bounds != null && !dockAreaOverlay.getBounds().equals(bounds)) { - if (animateBounds) { - PropertyValuesHolder prop = PropertyValuesHolder.ofObject( - Utilities.DRAWABLE_RECT, Utilities.RECT_EVALUATOR, - new Rect(dockAreaOverlay.getBounds()), bounds); - anim = ObjectAnimator.ofPropertyValuesHolder(dockAreaOverlay, prop); - anim.setDuration(duration); - anim.setInterpolator(interpolator); - animators.add(anim); - } else { - dockAreaOverlay.setBounds(bounds); - } - } - if (!animators.isEmpty()) { - mDockAreaOverlayAnimator = new AnimatorSet(); - mDockAreaOverlayAnimator.playTogether(animators); - mDockAreaOverlayAnimator.start(); - } - } - } - - public final int dockSide; - public final int createMode; - public final ViewState viewState; - private final RectF touchArea; - private final RectF dockArea; - private final RectF expandedTouchDockArea; - private static final Rect mTmpRect = new Rect(); - - /** - * @param createMode used to pass to ActivityManager to dock the task - * @param touchArea the area in which touch will initiate this dock state - * @param dockArea the visible dock area - * @param expandedTouchDockArea the area in which touch will continue to dock after entering - * the initial touch area. This is also the new dock area to - * draw. - */ - DockState(int dockSide, int createMode, int dockAreaAlpha, int hintTextAlpha, - @TextOrientation int hintTextOrientation, RectF touchArea, RectF dockArea, - RectF expandedTouchDockArea) { - this.dockSide = dockSide; - this.createMode = createMode; - this.viewState = new ViewState(dockAreaAlpha, hintTextAlpha, hintTextOrientation, - R.string.recents_drag_hint_message); - this.dockArea = dockArea; - this.touchArea = touchArea; - this.expandedTouchDockArea = expandedTouchDockArea; - } - - /** - * Updates the dock state with the given context. - */ - public void update(Context context) { - viewState.update(context); - } - - /** - * Returns the docked task bounds with the given {@param width} and {@param height}. - */ - public Rect getPreDockedBounds(int width, int height, Rect insets) { - getMappedRect(dockArea, width, height, mTmpRect); - return updateBoundsWithSystemInsets(mTmpRect, insets); - } - - /** - * Returns the expanded docked task bounds with the given {@param width} and - * {@param height}. - */ - public Rect getDockedBounds(int width, int height, int dividerSize, Rect insets, - Resources res) { - // Calculate the docked task bounds - boolean isHorizontalDivision = - res.getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT; - int position = DockedDividerUtils.calculateMiddlePosition(isHorizontalDivision, - insets, width, height, dividerSize); - Rect newWindowBounds = new Rect(); - DockedDividerUtils.calculateBoundsForPosition(position, dockSide, newWindowBounds, - width, height, dividerSize); - return newWindowBounds; - } - - /** - * Returns the task stack bounds with the given {@param width} and - * {@param height}. - */ - public Rect getDockedTaskStackBounds(Rect displayRect, int width, int height, - int dividerSize, Rect insets, TaskStackLayoutAlgorithm layoutAlgorithm, - Resources res, Rect windowRectOut) { - // Calculate the inverse docked task bounds - boolean isHorizontalDivision = - res.getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT; - int position = DockedDividerUtils.calculateMiddlePosition(isHorizontalDivision, - insets, width, height, dividerSize); - DockedDividerUtils.calculateBoundsForPosition(position, - DockedDividerUtils.invertDockSide(dockSide), windowRectOut, width, height, - dividerSize); - - // Calculate the task stack bounds from the new window bounds - Rect taskStackBounds = new Rect(); - // If the task stack bounds is specifically under the dock area, then ignore the top - // inset - int top = dockArea.bottom < 1f - ? 0 - : insets.top; - // For now, ignore the left insets since we always dock on the left and show Recents - // on the right - layoutAlgorithm.getTaskStackBounds(displayRect, windowRectOut, top, 0, insets.right, - taskStackBounds); - return taskStackBounds; - } - - /** - * Returns the expanded bounds in certain dock sides such that the bounds account for the - * system insets (namely the vertical nav bar). This call modifies and returns the given - * {@param bounds}. - */ - private Rect updateBoundsWithSystemInsets(Rect bounds, Rect insets) { - if (dockSide == DOCKED_LEFT) { - bounds.right += insets.left; - } else if (dockSide == DOCKED_RIGHT) { - bounds.left -= insets.right; - } - return bounds; - } - - /** - * Returns the mapped rect to the given dimensions. - */ - private void getMappedRect(RectF bounds, int width, int height, Rect out) { - out.set((int) (bounds.left * width), (int) (bounds.top * height), - (int) (bounds.right * width), (int) (bounds.bottom * height)); - } - } - - ArrayList<Task> mRawTaskList = new ArrayList<>(); - FilteredTaskList mStackTaskList = new FilteredTaskList(); - TaskStackCallbacks mCb; - - public TaskStack() { - // Ensure that we only show stack tasks - mStackTaskList.setFilter(new TaskFilter() { - @Override - public boolean acceptTask(SparseArray<Task> taskIdMap, Task t, int index) { - return t.isStackTask; - } - }); - } - - /** Sets the callbacks for this task stack. */ - public void setCallbacks(TaskStackCallbacks cb) { - mCb = cb; - } - - /** - * Removes a task from the stack, with an additional {@param animation} hint to the callbacks on - * how they should update themselves. - */ - public void removeTask(Task t, AnimationProps animation, boolean fromDockGesture) { - removeTask(t, animation, fromDockGesture, true /* dismissRecentsIfAllRemoved */); - } - - /** - * Removes a task from the stack, with an additional {@param animation} hint to the callbacks on - * how they should update themselves. - */ - public void removeTask(Task t, AnimationProps animation, boolean fromDockGesture, - boolean dismissRecentsIfAllRemoved) { - if (mStackTaskList.contains(t)) { - mStackTaskList.remove(t); - Task newFrontMostTask = getStackFrontMostTask(); - if (mCb != null) { - // Notify that a task has been removed - mCb.onStackTaskRemoved(this, t, newFrontMostTask, animation, - fromDockGesture, dismissRecentsIfAllRemoved); - } - } - mRawTaskList.remove(t); - } - - /** - * Removes all tasks from the stack. - */ - public void removeAllTasks(boolean notifyStackChanges) { - ArrayList<Task> tasks = mStackTaskList.getTasks(); - for (int i = tasks.size() - 1; i >= 0; i--) { - Task t = tasks.get(i); - mStackTaskList.remove(t); - mRawTaskList.remove(t); - } - if (mCb != null && notifyStackChanges) { - // Notify that all tasks have been removed - mCb.onStackTasksRemoved(this); - } - } - - - /** - * @see #setTasks(List, boolean) - */ - public void setTasks(TaskStack stack, boolean notifyStackChanges) { - setTasks(stack.mRawTaskList, notifyStackChanges); - } - - /** - * Sets a few tasks in one go, without calling any callbacks. - * - * @param tasks the new set of tasks to replace the current set. - * @param notifyStackChanges whether or not to callback on specific changes to the list of tasks. - */ - public void setTasks(List<Task> tasks, boolean notifyStackChanges) { - // Compute a has set for each of the tasks - ArrayMap<Task.TaskKey, Task> currentTasksMap = createTaskKeyMapFromList(mRawTaskList); - ArrayMap<Task.TaskKey, Task> newTasksMap = createTaskKeyMapFromList(tasks); - ArrayList<Task> addedTasks = new ArrayList<>(); - ArrayList<Task> removedTasks = new ArrayList<>(); - ArrayList<Task> allTasks = new ArrayList<>(); - - // Disable notifications if there are no callbacks - if (mCb == null) { - notifyStackChanges = false; - } - - // Remove any tasks that no longer exist - int taskCount = mRawTaskList.size(); - for (int i = taskCount - 1; i >= 0; i--) { - Task task = mRawTaskList.get(i); - if (!newTasksMap.containsKey(task.key)) { - if (notifyStackChanges) { - removedTasks.add(task); - } - } - } - - // Add any new tasks - taskCount = tasks.size(); - for (int i = 0; i < taskCount; i++) { - Task newTask = tasks.get(i); - Task currentTask = currentTasksMap.get(newTask.key); - if (currentTask == null && notifyStackChanges) { - addedTasks.add(newTask); - } else if (currentTask != null) { - // The current task has bound callbacks, so just copy the data from the new task - // state and add it back into the list - currentTask.copyFrom(newTask); - newTask = currentTask; - } - allTasks.add(newTask); - } - - // Sort all the tasks to ensure they are ordered correctly - for (int i = allTasks.size() - 1; i >= 0; i--) { - allTasks.get(i).temporarySortIndexInStack = i; - } - - mStackTaskList.set(allTasks); - mRawTaskList = allTasks; - - // Only callback for the removed tasks after the stack has updated - int removedTaskCount = removedTasks.size(); - Task newFrontMostTask = getStackFrontMostTask(); - for (int i = 0; i < removedTaskCount; i++) { - mCb.onStackTaskRemoved(this, removedTasks.get(i), newFrontMostTask, - AnimationProps.IMMEDIATE, false /* fromDockGesture */, - true /* dismissRecentsIfAllRemoved */); - } - - // Only callback for the newly added tasks after this stack has been updated - int addedTaskCount = addedTasks.size(); - for (int i = 0; i < addedTaskCount; i++) { - mCb.onStackTaskAdded(this, addedTasks.get(i)); - } - - // Notify that the task stack has been updated - if (notifyStackChanges) { - mCb.onStackTasksUpdated(this); - } - } - - /** - * Gets the front-most task in the stack. - */ - public Task getStackFrontMostTask() { - ArrayList<Task> stackTasks = mStackTaskList.getTasks(); - if (stackTasks.isEmpty()) { - return null; - } - return stackTasks.get(stackTasks.size() - 1); - } - - /** Gets the task keys */ - public ArrayList<Task.TaskKey> getTaskKeys() { - ArrayList<Task.TaskKey> taskKeys = new ArrayList<>(); - ArrayList<Task> tasks = computeAllTasksList(); - int taskCount = tasks.size(); - for (int i = 0; i < taskCount; i++) { - Task task = tasks.get(i); - taskKeys.add(task.key); - } - return taskKeys; - } - - /** - * Returns the set of "active" (non-historical) tasks in the stack that have been used recently. - */ - public ArrayList<Task> getStackTasks() { - return mStackTaskList.getTasks(); - } - - /** - * Computes a set of all the active and historical tasks. - */ - public ArrayList<Task> computeAllTasksList() { - ArrayList<Task> tasks = new ArrayList<>(); - tasks.addAll(mStackTaskList.getTasks()); - return tasks; - } - - /** - * Returns the number of stacktasks. - */ - public int getTaskCount() { - return mStackTaskList.size(); - } - - /** - * Returns the number of stack tasks. - */ - public int getStackTaskCount() { - return mStackTaskList.size(); - } - - /** - * Returns the task in stack tasks which is the launch target. - */ - public Task getLaunchTarget() { - ArrayList<Task> tasks = mStackTaskList.getTasks(); - int taskCount = tasks.size(); - for (int i = 0; i < taskCount; i++) { - Task task = tasks.get(i); - if (task.isLaunchTarget) { - return task; - } - } - return null; - } - - /** - * Returns whether the next launch target should actually be the PiP task. - */ - public boolean isNextLaunchTargetPip(long lastPipTime) { - Task launchTarget = getLaunchTarget(); - Task nextLaunchTarget = getNextLaunchTargetRaw(); - if (nextLaunchTarget != null && lastPipTime > 0) { - // If the PiP time is more recent than the next launch target, then launch the PiP task - return lastPipTime > nextLaunchTarget.key.lastActiveTime; - } else if (launchTarget != null && lastPipTime > 0 && getTaskCount() == 1) { - // Otherwise, if there is no next launch target, but there is a PiP, then launch - // the PiP task - return true; - } - return false; - } - - /** - * Returns the task in stack tasks which should be launched next if Recents are toggled - * again, or null if there is no task to be launched. Callers should check - * {@link #isNextLaunchTargetPip(long)} before fetching the next raw launch target from the - * stack. - */ - public Task getNextLaunchTarget() { - Task nextLaunchTarget = getNextLaunchTargetRaw(); - if (nextLaunchTarget != null) { - return nextLaunchTarget; - } - return getStackTasks().get(getTaskCount() - 1); - } - - private Task getNextLaunchTargetRaw() { - int taskCount = getTaskCount(); - if (taskCount == 0) { - return null; - } - int launchTaskIndex = indexOfStackTask(getLaunchTarget()); - if (launchTaskIndex != -1 && launchTaskIndex > 0) { - return getStackTasks().get(launchTaskIndex - 1); - } - return null; - } - - /** Returns the index of this task in this current task stack */ - public int indexOfStackTask(Task t) { - return mStackTaskList.indexOf(t); - } - - /** Finds the task with the specified task id. */ - public Task findTaskWithId(int taskId) { - ArrayList<Task> tasks = computeAllTasksList(); - int taskCount = tasks.size(); - for (int i = 0; i < taskCount; i++) { - Task task = tasks.get(i); - if (task.key.id == taskId) { - return task; - } - } - return null; - } - - /** - * Computes the components of tasks in this stack that have been removed as a result of a change - * in the specified package. - */ - public ArraySet<ComponentName> computeComponentsRemoved(String packageName, int userId) { - // Identify all the tasks that should be removed as a result of the package being removed. - // Using a set to ensure that we callback once per unique component. - SystemServicesProxy ssp = Recents.getSystemServices(); - ArraySet<ComponentName> existingComponents = new ArraySet<>(); - ArraySet<ComponentName> removedComponents = new ArraySet<>(); - ArrayList<Task.TaskKey> taskKeys = getTaskKeys(); - int taskKeyCount = taskKeys.size(); - for (int i = 0; i < taskKeyCount; i++) { - Task.TaskKey t = taskKeys.get(i); - - // Skip if this doesn't apply to the current user - if (t.userId != userId) continue; - - ComponentName cn = t.getComponent(); - if (cn.getPackageName().equals(packageName)) { - if (existingComponents.contains(cn)) { - // If we know that the component still exists in the package, then skip - continue; - } - if (ssp.getActivityInfo(cn, userId) != null) { - existingComponents.add(cn); - } else { - removedComponents.add(cn); - } - } - } - return removedComponents; - } - - @Override - public String toString() { - String str = "Stack Tasks (" + mStackTaskList.size() + "):\n"; - ArrayList<Task> tasks = mStackTaskList.getTasks(); - int taskCount = tasks.size(); - for (int i = 0; i < taskCount; i++) { - str += " " + tasks.get(i).toString() + "\n"; - } - return str; - } - - /** - * Given a list of tasks, returns a map of each task's key to the task. - */ - private ArrayMap<Task.TaskKey, Task> createTaskKeyMapFromList(List<Task> tasks) { - ArrayMap<Task.TaskKey, Task> map = new ArrayMap<>(tasks.size()); - int taskCount = tasks.size(); - for (int i = 0; i < taskCount; i++) { - Task task = tasks.get(i); - map.put(task.key, task); - } - return map; - } - - public void dump(String prefix, PrintWriter writer) { - String innerPrefix = prefix + " "; - - writer.print(prefix); writer.print(TAG); - writer.print(" numStackTasks="); writer.print(mStackTaskList.size()); - writer.println(); - ArrayList<Task> tasks = mStackTaskList.getTasks(); - int taskCount = tasks.size(); - for (int i = 0; i < taskCount; i++) { - tasks.get(i).dump(innerPrefix, writer); - } - } -} diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/AnimateableViewBounds.java b/packages/SystemUI/src/com/android/systemui/recents/views/AnimateableViewBounds.java index 7998ecb4290a..b598ec6fc31f 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/AnimateableViewBounds.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/AnimateableViewBounds.java @@ -22,7 +22,7 @@ import android.view.View; import android.view.ViewDebug; import android.view.ViewOutlineProvider; -import com.android.systemui.recents.misc.Utilities; +import com.android.systemui.shared.recents.utilities.Utilities; /* An outline provider that has a clip and outline that can be animated. */ public class AnimateableViewBounds extends ViewOutlineProvider { diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/DockState.java b/packages/SystemUI/src/com/android/systemui/recents/views/DockState.java new file mode 100644 index 000000000000..59f28680a6e0 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/recents/views/DockState.java @@ -0,0 +1,351 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.recents.views; + +import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT; +import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT; +import static android.view.WindowManager.DOCKED_BOTTOM; +import static android.view.WindowManager.DOCKED_INVALID; +import static android.view.WindowManager.DOCKED_LEFT; +import static android.view.WindowManager.DOCKED_RIGHT; +import static android.view.WindowManager.DOCKED_TOP; + +import android.animation.Animator; +import android.animation.AnimatorSet; +import android.animation.ObjectAnimator; +import android.animation.PropertyValuesHolder; +import android.annotation.IntDef; +import android.content.Context; +import android.content.res.Configuration; +import android.content.res.Resources; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Point; +import android.graphics.Rect; +import android.graphics.RectF; +import android.graphics.drawable.ColorDrawable; +import android.util.IntProperty; +import android.view.animation.Interpolator; + +import com.android.internal.policy.DockedDividerUtils; +import com.android.systemui.Interpolators; +import com.android.systemui.R; +import com.android.systemui.recents.Recents; +import com.android.systemui.shared.recents.utilities.Utilities; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.ArrayList; + +/** + * The various possible dock states when dragging and dropping a task. + */ +public class DockState implements DropTarget { + + public static final int DOCK_AREA_BG_COLOR = 0xFFffffff; + public static final int DOCK_AREA_GRID_BG_COLOR = 0xFF000000; + + // The rotation to apply to the hint text + @Retention(RetentionPolicy.SOURCE) + @IntDef({HORIZONTAL, VERTICAL}) + public @interface TextOrientation {} + private static final int HORIZONTAL = 0; + private static final int VERTICAL = 1; + + private static final int DOCK_AREA_ALPHA = 80; + public static final DockState NONE = new DockState(DOCKED_INVALID, -1, 80, 255, HORIZONTAL, + null, null, null); + public static final DockState LEFT = new DockState(DOCKED_LEFT, + DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT, DOCK_AREA_ALPHA, 0, VERTICAL, + new RectF(0, 0, 0.125f, 1), new RectF(0, 0, 0.125f, 1), + new RectF(0, 0, 0.5f, 1)); + public static final DockState TOP = new DockState(DOCKED_TOP, + DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT, DOCK_AREA_ALPHA, 0, HORIZONTAL, + new RectF(0, 0, 1, 0.125f), new RectF(0, 0, 1, 0.125f), + new RectF(0, 0, 1, 0.5f)); + public static final DockState RIGHT = new DockState(DOCKED_RIGHT, + DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT, DOCK_AREA_ALPHA, 0, VERTICAL, + new RectF(0.875f, 0, 1, 1), new RectF(0.875f, 0, 1, 1), + new RectF(0.5f, 0, 1, 1)); + public static final DockState BOTTOM = new DockState(DOCKED_BOTTOM, + DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT, DOCK_AREA_ALPHA, 0, HORIZONTAL, + new RectF(0, 0.875f, 1, 1), new RectF(0, 0.875f, 1, 1), + new RectF(0, 0.5f, 1, 1)); + + @Override + public boolean acceptsDrop(int x, int y, int width, int height, Rect insets, + boolean isCurrentTarget) { + if (isCurrentTarget) { + getMappedRect(expandedTouchDockArea, width, height, mTmpRect); + return mTmpRect.contains(x, y); + } else { + getMappedRect(touchArea, width, height, mTmpRect); + updateBoundsWithSystemInsets(mTmpRect, insets); + return mTmpRect.contains(x, y); + } + } + + // Represents the view state of this dock state + public static class ViewState { + private static final IntProperty<ViewState> HINT_ALPHA = + new IntProperty<ViewState>("drawableAlpha") { + @Override + public void setValue(ViewState object, int alpha) { + object.mHintTextAlpha = alpha; + object.dockAreaOverlay.invalidateSelf(); + } + + @Override + public Integer get(ViewState object) { + return object.mHintTextAlpha; + } + }; + + public final int dockAreaAlpha; + public final ColorDrawable dockAreaOverlay; + public final int hintTextAlpha; + public final int hintTextOrientation; + + private final int mHintTextResId; + private String mHintText; + private Paint mHintTextPaint; + private Point mHintTextBounds = new Point(); + private int mHintTextAlpha = 255; + private AnimatorSet mDockAreaOverlayAnimator; + private Rect mTmpRect = new Rect(); + + private ViewState(int areaAlpha, int hintAlpha, @TextOrientation int hintOrientation, + int hintTextResId) { + dockAreaAlpha = areaAlpha; + dockAreaOverlay = new ColorDrawable(Recents.getConfiguration().isGridEnabled + ? DOCK_AREA_GRID_BG_COLOR : DOCK_AREA_BG_COLOR); + dockAreaOverlay.setAlpha(0); + hintTextAlpha = hintAlpha; + hintTextOrientation = hintOrientation; + mHintTextResId = hintTextResId; + mHintTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + mHintTextPaint.setColor(Color.WHITE); + } + + /** + * Updates the view state with the given context. + */ + public void update(Context context) { + Resources res = context.getResources(); + mHintText = context.getString(mHintTextResId); + mHintTextPaint.setTextSize(res.getDimensionPixelSize( + R.dimen.recents_drag_hint_text_size)); + mHintTextPaint.getTextBounds(mHintText, 0, mHintText.length(), mTmpRect); + mHintTextBounds.set((int) mHintTextPaint.measureText(mHintText), mTmpRect.height()); + } + + /** + * Draws the current view state. + */ + public void draw(Canvas canvas) { + // Draw the overlay background + if (dockAreaOverlay.getAlpha() > 0) { + dockAreaOverlay.draw(canvas); + } + + // Draw the hint text + if (mHintTextAlpha > 0) { + Rect bounds = dockAreaOverlay.getBounds(); + int x = bounds.left + (bounds.width() - mHintTextBounds.x) / 2; + int y = bounds.top + (bounds.height() + mHintTextBounds.y) / 2; + mHintTextPaint.setAlpha(mHintTextAlpha); + if (hintTextOrientation == VERTICAL) { + canvas.save(); + canvas.rotate(-90f, bounds.centerX(), bounds.centerY()); + } + canvas.drawText(mHintText, x, y, mHintTextPaint); + if (hintTextOrientation == VERTICAL) { + canvas.restore(); + } + } + } + + /** + * Creates a new bounds and alpha animation. + */ + public void startAnimation(Rect bounds, int areaAlpha, int hintAlpha, int duration, + Interpolator interpolator, boolean animateAlpha, boolean animateBounds) { + if (mDockAreaOverlayAnimator != null) { + mDockAreaOverlayAnimator.cancel(); + } + + ObjectAnimator anim; + ArrayList<Animator> animators = new ArrayList<>(); + if (dockAreaOverlay.getAlpha() != areaAlpha) { + if (animateAlpha) { + anim = ObjectAnimator.ofInt(dockAreaOverlay, + Utilities.DRAWABLE_ALPHA, dockAreaOverlay.getAlpha(), areaAlpha); + anim.setDuration(duration); + anim.setInterpolator(interpolator); + animators.add(anim); + } else { + dockAreaOverlay.setAlpha(areaAlpha); + } + } + if (mHintTextAlpha != hintAlpha) { + if (animateAlpha) { + anim = ObjectAnimator.ofInt(this, HINT_ALPHA, mHintTextAlpha, + hintAlpha); + anim.setDuration(150); + anim.setInterpolator(hintAlpha > mHintTextAlpha + ? Interpolators.ALPHA_IN + : Interpolators.ALPHA_OUT); + animators.add(anim); + } else { + mHintTextAlpha = hintAlpha; + dockAreaOverlay.invalidateSelf(); + } + } + if (bounds != null && !dockAreaOverlay.getBounds().equals(bounds)) { + if (animateBounds) { + PropertyValuesHolder prop = PropertyValuesHolder.ofObject( + Utilities.DRAWABLE_RECT, Utilities.RECT_EVALUATOR, + new Rect(dockAreaOverlay.getBounds()), bounds); + anim = ObjectAnimator.ofPropertyValuesHolder(dockAreaOverlay, prop); + anim.setDuration(duration); + anim.setInterpolator(interpolator); + animators.add(anim); + } else { + dockAreaOverlay.setBounds(bounds); + } + } + if (!animators.isEmpty()) { + mDockAreaOverlayAnimator = new AnimatorSet(); + mDockAreaOverlayAnimator.playTogether(animators); + mDockAreaOverlayAnimator.start(); + } + } + } + + public final int dockSide; + public final int createMode; + public final ViewState viewState; + private final RectF touchArea; + private final RectF dockArea; + private final RectF expandedTouchDockArea; + private static final Rect mTmpRect = new Rect(); + + /** + * @param createMode used to pass to ActivityManager to dock the task + * @param touchArea the area in which touch will initiate this dock state + * @param dockArea the visible dock area + * @param expandedTouchDockArea the area in which touch will continue to dock after entering + * the initial touch area. This is also the new dock area to + * draw. + */ + DockState(int dockSide, int createMode, int dockAreaAlpha, int hintTextAlpha, + @TextOrientation int hintTextOrientation, RectF touchArea, RectF dockArea, + RectF expandedTouchDockArea) { + this.dockSide = dockSide; + this.createMode = createMode; + this.viewState = new ViewState(dockAreaAlpha, hintTextAlpha, hintTextOrientation, + R.string.recents_drag_hint_message); + this.dockArea = dockArea; + this.touchArea = touchArea; + this.expandedTouchDockArea = expandedTouchDockArea; + } + + /** + * Updates the dock state with the given context. + */ + public void update(Context context) { + viewState.update(context); + } + + /** + * Returns the docked task bounds with the given {@param width} and {@param height}. + */ + public Rect getPreDockedBounds(int width, int height, Rect insets) { + getMappedRect(dockArea, width, height, mTmpRect); + return updateBoundsWithSystemInsets(mTmpRect, insets); + } + + /** + * Returns the expanded docked task bounds with the given {@param width} and + * {@param height}. + */ + public Rect getDockedBounds(int width, int height, int dividerSize, Rect insets, + Resources res) { + // Calculate the docked task bounds + boolean isHorizontalDivision = + res.getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT; + int position = DockedDividerUtils.calculateMiddlePosition(isHorizontalDivision, + insets, width, height, dividerSize); + Rect newWindowBounds = new Rect(); + DockedDividerUtils.calculateBoundsForPosition(position, dockSide, newWindowBounds, + width, height, dividerSize); + return newWindowBounds; + } + + /** + * Returns the task stack bounds with the given {@param width} and + * {@param height}. + */ + public Rect getDockedTaskStackBounds(Rect displayRect, int width, int height, + int dividerSize, Rect insets, TaskStackLayoutAlgorithm layoutAlgorithm, + Resources res, Rect windowRectOut) { + // Calculate the inverse docked task bounds + boolean isHorizontalDivision = + res.getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT; + int position = DockedDividerUtils.calculateMiddlePosition(isHorizontalDivision, + insets, width, height, dividerSize); + DockedDividerUtils.calculateBoundsForPosition(position, + DockedDividerUtils.invertDockSide(dockSide), windowRectOut, width, height, + dividerSize); + + // Calculate the task stack bounds from the new window bounds + Rect taskStackBounds = new Rect(); + // If the task stack bounds is specifically under the dock area, then ignore the top + // inset + int top = dockArea.bottom < 1f + ? 0 + : insets.top; + // For now, ignore the left insets since we always dock on the left and show Recents + // on the right + layoutAlgorithm.getTaskStackBounds(displayRect, windowRectOut, top, 0, insets.right, + taskStackBounds); + return taskStackBounds; + } + + /** + * Returns the expanded bounds in certain dock sides such that the bounds account for the + * system insets (namely the vertical nav bar). This call modifies and returns the given + * {@param bounds}. + */ + private Rect updateBoundsWithSystemInsets(Rect bounds, Rect insets) { + if (dockSide == DOCKED_LEFT) { + bounds.right += insets.left; + } else if (dockSide == DOCKED_RIGHT) { + bounds.left -= insets.right; + } + return bounds; + } + + /** + * Returns the mapped rect to the given dimensions. + */ + private void getMappedRect(RectF bounds, int width, int height, Rect out) { + out.set((int) (bounds.left * width), (int) (bounds.top * height), + (int) (bounds.right * width), (int) (bounds.bottom * height)); + } +}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java index baa5e6273e5e..25c2fc97eda4 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java @@ -30,8 +30,6 @@ import android.app.ActivityOptions; import android.app.ActivityOptions.OnAnimationStartedListener; import android.content.Context; import android.graphics.Bitmap; -import android.graphics.Canvas; -import android.graphics.Color; import android.graphics.GraphicBuffer; import android.graphics.Rect; import android.os.Bundle; @@ -58,8 +56,8 @@ import com.android.systemui.recents.events.activity.LaunchTaskSucceededEvent; import com.android.systemui.recents.events.component.ScreenPinningRequestEvent; import com.android.systemui.recents.events.component.SetWaitingForTransitionStartEvent; import com.android.systemui.recents.misc.SystemServicesProxy; -import com.android.systemui.recents.model.Task; -import com.android.systemui.recents.model.TaskStack; +import com.android.systemui.shared.recents.model.Task; +import com.android.systemui.shared.recents.model.TaskStack; import com.android.systemui.statusbar.phone.StatusBar; import java.util.ArrayList; diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java index e1b22b4452fd..5f12a04b5e4d 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java @@ -53,7 +53,6 @@ import com.android.systemui.recents.Recents; import com.android.systemui.recents.RecentsActivity; import com.android.systemui.recents.RecentsActivityLaunchState; import com.android.systemui.recents.RecentsConfiguration; -import com.android.systemui.recents.RecentsDebugFlags; import com.android.systemui.recents.events.EventBus; import com.android.systemui.recents.events.activity.DismissRecentsToHomeAnimationStarted; import com.android.systemui.recents.events.activity.DockedFirstAnimationFrameEvent; @@ -74,9 +73,9 @@ import com.android.systemui.recents.events.ui.dragndrop.DragEndEvent; import com.android.systemui.recents.events.ui.dragndrop.DragStartEvent; import com.android.systemui.recents.misc.ReferenceCountedTrigger; import com.android.systemui.recents.misc.SystemServicesProxy; -import com.android.systemui.recents.misc.Utilities; -import com.android.systemui.recents.model.Task; -import com.android.systemui.recents.model.TaskStack; +import com.android.systemui.shared.recents.utilities.Utilities; +import com.android.systemui.shared.recents.model.Task; +import com.android.systemui.shared.recents.model.TaskStack; import com.android.systemui.recents.views.RecentsTransitionHelper.AnimationSpecComposer; import com.android.systemui.recents.views.RecentsTransitionHelper.AppTransitionAnimationSpecsFuture; import com.android.systemui.stackdivider.WindowManagerProxy; @@ -498,7 +497,7 @@ public class RecentsView extends FrameLayout { public void onDrawForeground(Canvas canvas) { super.onDrawForeground(canvas); - ArrayList<TaskStack.DockState> visDockStates = mTouchHandler.getVisibleDockStates(); + ArrayList<DockState> visDockStates = mTouchHandler.getVisibleDockStates(); for (int i = visDockStates.size() - 1; i >= 0; i--) { visDockStates.get(i).viewState.draw(canvas); } @@ -506,7 +505,7 @@ public class RecentsView extends FrameLayout { @Override protected boolean verifyDrawable(Drawable who) { - ArrayList<TaskStack.DockState> visDockStates = mTouchHandler.getVisibleDockStates(); + ArrayList<DockState> visDockStates = mTouchHandler.getVisibleDockStates(); for (int i = visDockStates.size() - 1; i >= 0; i--) { Drawable d = visDockStates.get(i).viewState.dockAreaOverlay; if (d == who) { @@ -540,8 +539,8 @@ public class RecentsView extends FrameLayout { public final void onBusEvent(DragStartEvent event) { updateVisibleDockRegions(Recents.getConfiguration().getDockStatesForCurrentOrientation(), - true /* isDefaultDockState */, TaskStack.DockState.NONE.viewState.dockAreaAlpha, - TaskStack.DockState.NONE.viewState.hintTextAlpha, + true /* isDefaultDockState */, DockState.NONE.viewState.dockAreaAlpha, + DockState.NONE.viewState.hintTextAlpha, true /* animateAlpha */, false /* animateBounds */); // Temporarily hide the stack action button without changing visibility @@ -555,15 +554,15 @@ public class RecentsView extends FrameLayout { } public final void onBusEvent(DragDropTargetChangedEvent event) { - if (event.dropTarget == null || !(event.dropTarget instanceof TaskStack.DockState)) { + if (event.dropTarget == null || !(event.dropTarget instanceof DockState)) { updateVisibleDockRegions( Recents.getConfiguration().getDockStatesForCurrentOrientation(), - true /* isDefaultDockState */, TaskStack.DockState.NONE.viewState.dockAreaAlpha, - TaskStack.DockState.NONE.viewState.hintTextAlpha, + true /* isDefaultDockState */, DockState.NONE.viewState.dockAreaAlpha, + DockState.NONE.viewState.hintTextAlpha, true /* animateAlpha */, true /* animateBounds */); } else { - final TaskStack.DockState dockState = (TaskStack.DockState) event.dropTarget; - updateVisibleDockRegions(new TaskStack.DockState[] {dockState}, + final DockState dockState = (DockState) event.dropTarget; + updateVisibleDockRegions(new DockState[] {dockState}, false /* isDefaultDockState */, -1, -1, true /* animateAlpha */, true /* animateBounds */); } @@ -582,8 +581,8 @@ public class RecentsView extends FrameLayout { public final void onBusEvent(final DragEndEvent event) { // Handle the case where we drop onto a dock region - if (event.dropTarget instanceof TaskStack.DockState) { - final TaskStack.DockState dockState = (TaskStack.DockState) event.dropTarget; + if (event.dropTarget instanceof DockState) { + final DockState dockState = (DockState) event.dropTarget; // Hide the dock region updateVisibleDockRegions(null, false /* isDefaultDockState */, -1, -1, @@ -812,15 +811,15 @@ public class RecentsView extends FrameLayout { /** * Updates the dock region to match the specified dock state. */ - private void updateVisibleDockRegions(TaskStack.DockState[] newDockStates, + private void updateVisibleDockRegions(DockState[] newDockStates, boolean isDefaultDockState, int overrideAreaAlpha, int overrideHintAlpha, boolean animateAlpha, boolean animateBounds) { - ArraySet<TaskStack.DockState> newDockStatesSet = Utilities.arrayToSet(newDockStates, - new ArraySet<TaskStack.DockState>()); - ArrayList<TaskStack.DockState> visDockStates = mTouchHandler.getVisibleDockStates(); + ArraySet<DockState> newDockStatesSet = Utilities.arrayToSet(newDockStates, + new ArraySet<DockState>()); + ArrayList<DockState> visDockStates = mTouchHandler.getVisibleDockStates(); for (int i = visDockStates.size() - 1; i >= 0; i--) { - TaskStack.DockState dockState = visDockStates.get(i); - TaskStack.DockState.ViewState viewState = dockState.viewState; + DockState dockState = visDockStates.get(i); + DockState.ViewState viewState = dockState.viewState; if (newDockStates == null || !newDockStatesSet.contains(dockState)) { // This is no longer visible, so hide it viewState.startAnimation(null, 0, 0, TaskStackView.SLOW_SYNC_STACK_DURATION, diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java index b6b24bcd8800..0cfdbdecdf2a 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java @@ -17,7 +17,6 @@ package com.android.systemui.recents.views; import android.app.ActivityManager; -import android.content.res.Configuration; import android.graphics.Point; import android.graphics.Rect; import android.view.InputDevice; @@ -32,7 +31,6 @@ import com.android.systemui.recents.Recents; import com.android.systemui.recents.events.EventBus; import com.android.systemui.recents.events.activity.ConfigurationChangedEvent; import com.android.systemui.recents.events.activity.HideRecentsEvent; -import com.android.systemui.recents.events.activity.HideStackActionButtonEvent; import com.android.systemui.recents.events.ui.DismissAllTaskViewsEvent; import com.android.systemui.recents.events.ui.HideIncompatibleAppOverlayEvent; import com.android.systemui.recents.events.ui.ShowIncompatibleAppOverlayEvent; @@ -41,8 +39,8 @@ import com.android.systemui.recents.events.ui.dragndrop.DragEndEvent; import com.android.systemui.recents.events.ui.dragndrop.DragStartEvent; import com.android.systemui.recents.events.ui.dragndrop.DragStartInitializeDropTargetsEvent; import com.android.systemui.recents.misc.SystemServicesProxy; -import com.android.systemui.recents.model.Task; -import com.android.systemui.recents.model.TaskStack; +import com.android.systemui.shared.recents.model.Task; +import com.android.systemui.shared.recents.model.TaskStack; import java.util.ArrayList; @@ -72,7 +70,7 @@ public class RecentsViewTouchHandler { private DropTarget mLastDropTarget; private DividerSnapAlgorithm mDividerSnapAlgorithm; private ArrayList<DropTarget> mDropTargets = new ArrayList<>(); - private ArrayList<TaskStack.DockState> mVisibleDockStates = new ArrayList<>(); + private ArrayList<DockState> mVisibleDockStates = new ArrayList<>(); public RecentsViewTouchHandler(RecentsView rv) { mRv = rv; @@ -96,7 +94,7 @@ public class RecentsViewTouchHandler { /** * Returns the set of visible dock states for this current drag. */ - public ArrayList<TaskStack.DockState> getVisibleDockStates() { + public ArrayList<DockState> getVisibleDockStates() { return mVisibleDockStates; } @@ -148,9 +146,9 @@ public class RecentsViewTouchHandler { EventBus.getDefault().send(new ShowIncompatibleAppOverlayEvent()); } else { // Add the dock state drop targets (these take priority) - TaskStack.DockState[] dockStates = Recents.getConfiguration() + DockState[] dockStates = Recents.getConfiguration() .getDockStatesForCurrentOrientation(); - for (TaskStack.DockState dockState : dockStates) { + for (DockState dockState : dockStates) { registerDropTargetForCurrentDrag(dockState); dockState.update(mRv.getContext()); mVisibleDockStates.add(dockState); diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/SystemBarScrimViews.java b/packages/SystemUI/src/com/android/systemui/recents/views/SystemBarScrimViews.java index 8f784b832e4c..7827c590ed81 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/SystemBarScrimViews.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/SystemBarScrimViews.java @@ -30,7 +30,7 @@ import com.android.systemui.recents.events.ui.DismissAllTaskViewsEvent; import com.android.systemui.recents.events.activity.MultiWindowStateChangedEvent; import com.android.systemui.recents.events.ui.dragndrop.DragEndCancelledEvent; import com.android.systemui.recents.events.ui.dragndrop.DragEndEvent; -import com.android.systemui.recents.model.TaskStack; +import com.android.systemui.shared.recents.utilities.AnimationProps; /** Manages the scrims for the various system bars. */ public class SystemBarScrimViews { @@ -159,7 +159,7 @@ public class SystemBarScrimViews { public final void onBusEvent(final DragEndEvent event) { // Hide the nav bar scrims once we drop to a dock region - if (event.dropTarget instanceof TaskStack.DockState) { + if (event.dropTarget instanceof DockState) { animateScrimToCurrentNavBarState(false /* hasStackTasks */); } } diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackAnimationHelper.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackAnimationHelper.java index f47c1d2877bf..26db26fa3c36 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackAnimationHelper.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackAnimationHelper.java @@ -18,13 +18,11 @@ package com.android.systemui.recents.views; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; -import android.animation.TimeInterpolator; import android.animation.ValueAnimator; import android.content.Context; import android.content.res.Configuration; import android.content.res.Resources; import android.util.Log; -import android.view.View; import android.view.animation.Interpolator; import android.view.animation.PathInterpolator; @@ -37,9 +35,10 @@ import com.android.systemui.recents.RecentsDebugFlags; import com.android.systemui.recents.events.EventBus; import com.android.systemui.recents.events.component.SetWaitingForTransitionStartEvent; import com.android.systemui.recents.misc.ReferenceCountedTrigger; -import com.android.systemui.recents.model.Task; -import com.android.systemui.recents.model.TaskStack; +import com.android.systemui.shared.recents.model.Task; +import com.android.systemui.shared.recents.model.TaskStack; import com.android.systemui.recents.views.lowram.TaskStackLowRamLayoutAlgorithm; +import com.android.systemui.shared.recents.utilities.AnimationProps; import java.util.ArrayList; import java.util.List; diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java index 5ba5f44a4433..acb058cee716 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java @@ -24,7 +24,6 @@ import android.graphics.Path; import android.graphics.Rect; import android.util.ArraySet; import android.util.Log; -import android.util.MutableFloat; import android.util.SparseArray; import android.util.SparseIntArray; import android.view.ViewDebug; @@ -36,9 +35,9 @@ import com.android.systemui.recents.RecentsConfiguration; import com.android.systemui.recents.RecentsDebugFlags; import com.android.systemui.recents.misc.FreePathInterpolator; import com.android.systemui.recents.misc.SystemServicesProxy; -import com.android.systemui.recents.misc.Utilities; -import com.android.systemui.recents.model.Task; -import com.android.systemui.recents.model.TaskStack; +import com.android.systemui.shared.recents.utilities.Utilities; +import com.android.systemui.shared.recents.model.Task; +import com.android.systemui.shared.recents.model.TaskStack; import com.android.systemui.recents.views.lowram.TaskStackLowRamLayoutAlgorithm; import com.android.systemui.recents.views.grid.TaskGridLayoutAlgorithm; @@ -505,9 +504,7 @@ public class TaskStackLayoutAlgorithm { boolean scrollToFront = launchState.launchedFromHome || launchState.launchedFromPipApp || launchState.launchedWithNextPipApp || launchState.launchedViaDockGesture; - if (launchState.launchedFromBlacklistedApp) { - mInitialScrollP = mMaxScrollP; - } else if (launchState.launchedWithAltTab) { + if (launchState.launchedWithAltTab) { mInitialScrollP = Utilities.clamp(launchTaskIndex, mMinScrollP, mMaxScrollP); } else if (Recents.getConfiguration().isLowRamDevice) { mInitialScrollP = mTaskStackLowRamLayoutAlgorithm.getInitialScrollP(mNumStackTasks, @@ -535,7 +532,6 @@ public class TaskStackLayoutAlgorithm { boolean scrollToFront = launchState.launchedFromHome || launchState.launchedFromPipApp || launchState.launchedWithNextPipApp || - launchState.launchedFromBlacklistedApp || launchState.launchedViaDockGesture; if (getInitialFocusState() == STATE_UNFOCUSED && mNumStackTasks > 1) { if (ignoreScrollToFront || (!launchState.launchedWithAltTab && !scrollToFront)) { diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java index cda5fb825d5f..428113a2a065 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java @@ -87,9 +87,10 @@ import com.android.systemui.recents.events.ui.focus.NavigateTaskViewEvent; import com.android.systemui.recents.misc.DozeTrigger; import com.android.systemui.recents.misc.ReferenceCountedTrigger; import com.android.systemui.recents.misc.SystemServicesProxy; -import com.android.systemui.recents.misc.Utilities; -import com.android.systemui.recents.model.Task; -import com.android.systemui.recents.model.TaskStack; +import com.android.systemui.shared.recents.utilities.AnimationProps; +import com.android.systemui.shared.recents.utilities.Utilities; +import com.android.systemui.shared.recents.model.Task; +import com.android.systemui.shared.recents.model.TaskStack; import com.android.systemui.recents.views.grid.GridTaskView; import com.android.systemui.recents.views.grid.TaskGridLayoutAlgorithm; import com.android.systemui.recents.views.grid.TaskViewFocusFrame; @@ -470,7 +471,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal boolean useTargetStackScroll = Float.compare(curStackScroll, targetStackScroll) != 0; // We can reuse the task transforms where possible to reduce object allocation - Utilities.matchTaskListSize(tasks, taskTransforms); + matchTaskListSize(tasks, taskTransforms); // Update the stack transforms TaskViewTransform frontTransform = null; @@ -700,7 +701,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal */ public void getCurrentTaskTransforms(ArrayList<Task> tasks, ArrayList<TaskViewTransform> transformsOut) { - Utilities.matchTaskListSize(tasks, transformsOut); + matchTaskListSize(tasks, transformsOut); int focusState = mLayoutAlgorithm.getFocusState(); for (int i = tasks.size() - 1; i >= 0; i--) { Task task = tasks.get(i); @@ -723,7 +724,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal */ public void getLayoutTaskTransforms(float stackScroll, int focusState, ArrayList<Task> tasks, boolean ignoreTaskOverrides, ArrayList<TaskViewTransform> transformsOut) { - Utilities.matchTaskListSize(tasks, transformsOut); + matchTaskListSize(tasks, transformsOut); for (int i = tasks.size() - 1; i >= 0; i--) { Task task = tasks.get(i); TaskViewTransform transform = transformsOut.get(i); @@ -1901,10 +1902,10 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal AnimationProps animation = new AnimationProps(SLOW_SYNC_STACK_DURATION, Interpolators.FAST_OUT_SLOW_IN); boolean ignoreTaskOverrides = false; - if (event.dropTarget instanceof TaskStack.DockState) { + if (event.dropTarget instanceof DockState) { // Calculate the new task stack bounds that matches the window size that Recents will // have after the drop - final TaskStack.DockState dockState = (TaskStack.DockState) event.dropTarget; + final DockState dockState = (DockState) event.dropTarget; Rect systemInsets = new Rect(mStableLayoutAlgorithm.mSystemInsets); // When docked, the nav bar insets are consumed and the activity is measured without // insets. However, the window bounds include the insets, so we need to subtract them @@ -1931,7 +1932,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal public final void onBusEvent(final DragEndEvent event) { // We don't handle drops on the dock regions - if (event.dropTarget instanceof TaskStack.DockState) { + if (event.dropTarget instanceof DockState) { // However, we do need to reset the overrides, since the last state of this task stack // view layout was ignoring task overrides (see DragDropTargetChangedEvent handler) mLayoutAlgorithm.clearUnfocusedTaskOverrides(); @@ -2199,6 +2200,24 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal } } + /** + * Updates {@param transforms} to be the same size as {@param tasks}. + */ + private void matchTaskListSize(List<Task> tasks, List<TaskViewTransform> transforms) { + // We can reuse the task transforms where possible to reduce object allocation + int taskTransformCount = transforms.size(); + int taskCount = tasks.size(); + if (taskTransformCount < taskCount) { + // If there are less transforms than tasks, then add as many transforms as necessary + for (int i = taskTransformCount; i < taskCount; i++) { + transforms.add(new TaskViewTransform()); + } + } else if (taskTransformCount > taskCount) { + // If there are more transforms than tasks, then just subset the transform list + transforms.subList(taskCount, taskTransformCount).clear(); + } + } + public void dump(String prefix, PrintWriter writer) { String innerPrefix = prefix + " "; String id = Integer.toHexString(System.identityHashCode(this)); diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java index 0b20b105617d..6b23977410c7 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java @@ -24,7 +24,6 @@ import android.animation.ValueAnimator; import android.content.Context; import android.util.FloatProperty; import android.util.Log; -import android.util.MutableFloat; import android.util.Property; import android.view.ViewConfiguration; import android.view.ViewDebug; @@ -33,7 +32,8 @@ import android.widget.OverScroller; import com.android.systemui.Interpolators; import com.android.systemui.R; import com.android.systemui.recents.Recents; -import com.android.systemui.recents.misc.Utilities; +import com.android.systemui.shared.recents.utilities.AnimationProps; +import com.android.systemui.shared.recents.utilities.Utilities; import com.android.systemui.recents.views.lowram.TaskStackLowRamLayoutAlgorithm; import com.android.systemui.statusbar.FlingAnimationUtils; diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java index 1abaced83969..b9ca2483f3be 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java @@ -21,7 +21,6 @@ import android.animation.ValueAnimator; import android.content.Context; import android.content.res.Resources; import android.graphics.Path; -import android.graphics.Rect; import android.util.ArrayMap; import android.util.MutableBoolean; import android.view.InputDevice; @@ -45,9 +44,9 @@ import com.android.systemui.recents.events.activity.HideRecentsEvent; import com.android.systemui.recents.events.ui.StackViewScrolledEvent; import com.android.systemui.recents.events.ui.TaskViewDismissedEvent; import com.android.systemui.recents.misc.FreePathInterpolator; -import com.android.systemui.recents.misc.SystemServicesProxy; -import com.android.systemui.recents.misc.Utilities; -import com.android.systemui.recents.model.Task; +import com.android.systemui.shared.recents.utilities.AnimationProps; +import com.android.systemui.shared.recents.utilities.Utilities; +import com.android.systemui.shared.recents.model.Task; import com.android.systemui.statusbar.FlingAnimationUtils; import java.util.ArrayList; diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java index a75034a123ee..b44084743896 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java @@ -51,10 +51,10 @@ import com.android.systemui.recents.events.ui.dragndrop.DragEndEvent; import com.android.systemui.recents.events.ui.dragndrop.DragStartEvent; import com.android.systemui.recents.misc.ReferenceCountedTrigger; import com.android.systemui.recents.misc.SystemServicesProxy; -import com.android.systemui.recents.misc.Utilities; -import com.android.systemui.recents.model.Task; -import com.android.systemui.recents.model.TaskStack; -import com.android.systemui.recents.model.ThumbnailData; +import com.android.systemui.shared.recents.utilities.AnimationProps; +import com.android.systemui.shared.recents.utilities.Utilities; +import com.android.systemui.shared.recents.model.Task; +import com.android.systemui.shared.recents.model.ThumbnailData; import java.io.PrintWriter; import java.util.ArrayList; @@ -709,7 +709,7 @@ public class TaskView extends FixedSizeFrameLayout implements Task.TaskCallbacks /**** Events ****/ public final void onBusEvent(DragEndEvent event) { - if (!(event.dropTarget instanceof TaskStack.DockState)) { + if (!(event.dropTarget instanceof DockState)) { event.addPostAnimationCallback(() -> { // Reset the clip state for the drag view after the end animation completes setClipViewInStack(true); diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewAccessibilityDelegate.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewAccessibilityDelegate.java index 0c6b6b842655..0fc507b92bf3 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewAccessibilityDelegate.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewAccessibilityDelegate.java @@ -28,11 +28,10 @@ import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction; import com.android.systemui.R; import com.android.systemui.recents.Recents; import com.android.systemui.recents.events.EventBus; -import com.android.systemui.recents.events.ui.ShowApplicationInfoEvent; import com.android.systemui.recents.events.ui.dragndrop.DragEndEvent; import com.android.systemui.recents.events.ui.dragndrop.DragStartEvent; -import com.android.systemui.recents.misc.Utilities; -import com.android.systemui.recents.model.TaskStack; +import com.android.systemui.shared.recents.utilities.Utilities; +import com.android.systemui.shared.recents.model.TaskStack; public class TaskViewAccessibilityDelegate extends View.AccessibilityDelegate { private static final String TAG = "TaskViewAccessibilityDelegate"; @@ -61,14 +60,14 @@ public class TaskViewAccessibilityDelegate extends View.AccessibilityDelegate { super.onInitializeAccessibilityNodeInfo(host, info); if (ActivityManager.supportsSplitScreenMultiWindow(mTaskView.getContext()) && !Recents.getSystemServices().hasDockedTask()) { - TaskStack.DockState[] dockStates = Recents.getConfiguration() + DockState[] dockStates = Recents.getConfiguration() .getDockStatesForCurrentOrientation(); - for (TaskStack.DockState dockState: dockStates) { - if (dockState == TaskStack.DockState.TOP) { + for (DockState dockState: dockStates) { + if (dockState == DockState.TOP) { info.addAction(mActions.get(SPLIT_TASK_TOP)); - } else if (dockState == TaskStack.DockState.LEFT) { + } else if (dockState == DockState.LEFT) { info.addAction(mActions.get(SPLIT_TASK_LEFT)); - } else if (dockState == TaskStack.DockState.RIGHT) { + } else if (dockState == DockState.RIGHT) { info.addAction(mActions.get(SPLIT_TASK_RIGHT)); } } @@ -78,11 +77,11 @@ public class TaskViewAccessibilityDelegate extends View.AccessibilityDelegate { @Override public boolean performAccessibilityAction(View host, int action, Bundle args) { if (action == SPLIT_TASK_TOP) { - simulateDragIntoMultiwindow(TaskStack.DockState.TOP); + simulateDragIntoMultiwindow(DockState.TOP); } else if (action == SPLIT_TASK_LEFT) { - simulateDragIntoMultiwindow(TaskStack.DockState.LEFT); + simulateDragIntoMultiwindow(DockState.LEFT); } else if (action == SPLIT_TASK_RIGHT) { - simulateDragIntoMultiwindow(TaskStack.DockState.RIGHT); + simulateDragIntoMultiwindow(DockState.RIGHT); } else { return super.performAccessibilityAction(host, action, args); } @@ -90,8 +89,7 @@ public class TaskViewAccessibilityDelegate extends View.AccessibilityDelegate { } /** Simulate a user drag event to split the screen to the respected side */ - private void simulateDragIntoMultiwindow(TaskStack.DockState dockState) { - int orientation = Utilities.getAppConfiguration(mTaskView.getContext()).orientation; + private void simulateDragIntoMultiwindow(DockState dockState) { EventBus.getDefault().send(new DragStartEvent(mTaskView.getTask(), mTaskView, new Point(0,0), false /* isUserTouchInitiated */)); EventBus.getDefault().send(new DragEndEvent(mTaskView.getTask(), mTaskView, dockState)); diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java index 1420a0125f20..0272a9038ba8 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java @@ -17,7 +17,6 @@ package com.android.systemui.recents.views; import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; -import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import android.animation.Animator; @@ -32,7 +31,6 @@ import android.graphics.Color; import android.graphics.ColorFilter; import android.graphics.Paint; import android.graphics.PixelFormat; -import android.graphics.PorterDuff; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.graphics.drawable.RippleDrawable; @@ -58,8 +56,10 @@ import com.android.systemui.recents.events.EventBus; import com.android.systemui.recents.events.activity.LaunchTaskEvent; import com.android.systemui.recents.events.ui.ShowApplicationInfoEvent; import com.android.systemui.recents.misc.SystemServicesProxy; -import com.android.systemui.recents.misc.Utilities; -import com.android.systemui.recents.model.Task; +import com.android.systemui.shared.system.ActivityManagerWrapper; +import com.android.systemui.shared.system.PackageManagerWrapper; +import com.android.systemui.shared.recents.utilities.Utilities; +import com.android.systemui.shared.recents.model.Task; /* The task bar view */ public class TaskViewHeader extends FrameLayout @@ -170,6 +170,8 @@ public class TaskViewHeader extends FrameLayout int mTaskBarViewLightTextColor; int mTaskBarViewDarkTextColor; int mDisabledTaskBarBackgroundColor; + String mDismissDescFormat; + String mAppInfoDescFormat; int mTaskWindowingMode = WINDOWING_MODE_UNDEFINED; // Header background @@ -218,6 +220,9 @@ public class TaskViewHeader extends FrameLayout mDarkInfoIcon = context.getDrawable(R.drawable.recents_info_dark); mDisabledTaskBarBackgroundColor = context.getColor(R.color.recents_task_bar_disabled_background_color); + mDismissDescFormat = mContext.getString( + R.string.accessibility_recents_item_will_be_dismissed); + mAppInfoDescFormat = mContext.getString(R.string.accessibility_recents_item_open_app_info); // Configure the background and dim mBackground = new HighlightColorDrawable(); @@ -455,14 +460,14 @@ public class TaskViewHeader extends FrameLayout mTaskBarViewLightTextColor : mTaskBarViewDarkTextColor); mDismissButton.setImageDrawable(t.useLightOnPrimaryColor ? mLightDismissDrawable : mDarkDismissDrawable); - mDismissButton.setContentDescription(t.dismissDescription); + mDismissButton.setContentDescription(String.format(mDismissDescFormat, t.titleDescription)); mDismissButton.setOnClickListener(this); mDismissButton.setClickable(false); ((RippleDrawable) mDismissButton.getBackground()).setForceSoftware(true); // In accessibility, a single click on the focused app info button will show it if (touchExplorationEnabled) { - mIconView.setContentDescription(t.appInfoDescription); + mIconView.setContentDescription(String.format(mAppInfoDescFormat, t.titleDescription)); mIconView.setOnClickListener(this); mIconView.setClickable(true); } @@ -599,7 +604,7 @@ public class TaskViewHeader extends FrameLayout SystemServicesProxy ssp = Recents.getSystemServices(); ComponentName cn = mTask.key.getComponent(); int userId = mTask.key.userId; - ActivityInfo activityInfo = ssp.getActivityInfo(cn, userId); + ActivityInfo activityInfo = PackageManagerWrapper.getInstance().getActivityInfo(cn, userId); if (activityInfo == null) { return; } @@ -619,11 +624,12 @@ public class TaskViewHeader extends FrameLayout } // Update the overlay contents for the current app - mAppTitleView.setText(ssp.getBadgedApplicationLabel(activityInfo.applicationInfo, userId)); + mAppTitleView.setText(ActivityManagerWrapper.getInstance().getBadgedApplicationLabel( + activityInfo.applicationInfo, userId)); mAppTitleView.setTextColor(mTask.useLightOnPrimaryColor ? mTaskBarViewLightTextColor : mTaskBarViewDarkTextColor); - mAppIconView.setImageDrawable(ssp.getBadgedApplicationIcon(activityInfo.applicationInfo, - userId)); + mAppIconView.setImageDrawable(ActivityManagerWrapper.getInstance().getBadgedApplicationIcon( + activityInfo.applicationInfo, userId)); mAppInfoView.setImageDrawable(mTask.useLightOnPrimaryColor ? mLightInfoIcon : mDarkInfoIcon); diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java index d0ebc8dfc21f..4152b05a960e 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java @@ -37,9 +37,9 @@ import android.view.ViewDebug; import com.android.systemui.R; import com.android.systemui.recents.events.EventBus; import com.android.systemui.recents.events.ui.TaskSnapshotChangedEvent; -import com.android.systemui.recents.misc.Utilities; -import com.android.systemui.recents.model.Task; -import com.android.systemui.recents.model.ThumbnailData; +import com.android.systemui.shared.recents.utilities.Utilities; +import com.android.systemui.shared.recents.model.Task; +import com.android.systemui.shared.recents.model.ThumbnailData; import java.io.PrintWriter; diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java index c9dbe2ad2c4a..9b717e0e5e2f 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java @@ -21,11 +21,11 @@ import android.animation.ObjectAnimator; import android.animation.PropertyValuesHolder; import android.graphics.Rect; import android.graphics.RectF; -import android.util.IntProperty; import android.util.Property; import android.view.View; -import com.android.systemui.recents.misc.Utilities; +import com.android.systemui.shared.recents.utilities.AnimationProps; +import com.android.systemui.shared.recents.utilities.Utilities; import java.util.ArrayList; diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/grid/TaskGridLayoutAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/grid/TaskGridLayoutAlgorithm.java index c5132024d505..ccda4b5aaf1f 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/grid/TaskGridLayoutAlgorithm.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/grid/TaskGridLayoutAlgorithm.java @@ -25,10 +25,9 @@ import android.graphics.Rect; import android.view.WindowManager; import com.android.systemui.R; -import com.android.systemui.recents.events.ui.focus.NavigateTaskViewEvent; import com.android.systemui.recents.events.ui.focus.NavigateTaskViewEvent.Direction; -import com.android.systemui.recents.misc.Utilities; -import com.android.systemui.recents.model.Task; +import com.android.systemui.shared.recents.utilities.Utilities; +import com.android.systemui.shared.recents.model.Task; import com.android.systemui.recents.views.TaskStackLayoutAlgorithm; import com.android.systemui.recents.views.TaskViewTransform; diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/grid/TaskViewFocusFrame.java b/packages/SystemUI/src/com/android/systemui/recents/views/grid/TaskViewFocusFrame.java index 86ed583b07aa..95f1d5837e8e 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/grid/TaskViewFocusFrame.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/grid/TaskViewFocusFrame.java @@ -23,7 +23,7 @@ import android.view.View; import android.view.ViewTreeObserver.OnGlobalFocusChangeListener; import com.android.systemui.R; -import com.android.systemui.recents.model.TaskStack; +import com.android.systemui.shared.recents.model.TaskStack; import com.android.systemui.recents.views.TaskStackView; public class TaskViewFocusFrame extends View implements OnGlobalFocusChangeListener { diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/lowram/TaskStackLowRamLayoutAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/lowram/TaskStackLowRamLayoutAlgorithm.java index 17e6b9e3c195..49cac269f51d 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/lowram/TaskStackLowRamLayoutAlgorithm.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/lowram/TaskStackLowRamLayoutAlgorithm.java @@ -23,8 +23,8 @@ import android.view.ViewConfiguration; import com.android.systemui.R; import com.android.systemui.recents.Recents; import com.android.systemui.recents.RecentsActivityLaunchState; -import com.android.systemui.recents.misc.Utilities; -import com.android.systemui.recents.model.Task; +import com.android.systemui.shared.recents.utilities.Utilities; +import com.android.systemui.shared.recents.model.Task; import com.android.systemui.recents.views.TaskStackLayoutAlgorithm; import com.android.systemui.recents.views.TaskViewTransform; diff --git a/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java b/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java index 9211e3f6e86c..195f4d3f480d 100644 --- a/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java +++ b/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java @@ -20,37 +20,24 @@ import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIG import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT; import static android.os.UserHandle.USER_CURRENT; -import android.accessibilityservice.AccessibilityServiceInfo; import android.app.ActivityManager; -import android.app.IActivityManager; -import android.content.ComponentName; -import android.content.Context; -import android.content.pm.ServiceInfo; import android.content.res.Configuration; import android.os.RemoteException; -import android.os.UserHandle; -import android.util.ArraySet; -import android.util.DisplayMetrics; import android.util.Log; import android.view.IWindowManager; import android.view.KeyEvent; import android.view.WindowManager; import android.view.WindowManagerGlobal; -import android.view.accessibility.AccessibilityManager; -import com.android.internal.logging.MetricsLogger; -import com.android.internal.logging.nano.MetricsProto.MetricsEvent; + import com.android.internal.policy.DividerSnapAlgorithm; -import com.android.settingslib.accessibility.AccessibilityUtils; -import com.android.systemui.R; import com.android.systemui.SystemUI; import com.android.systemui.recents.Recents; import com.android.systemui.recents.misc.SystemServicesProxy; +import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.stackdivider.Divider; import com.android.systemui.stackdivider.DividerView; -import com.android.systemui.statusbar.phone.NavigationBarGestureHelper; import java.util.List; -import java.util.Set; /** * Dispatches shortcut to System UI components @@ -62,7 +49,6 @@ public class ShortcutKeyDispatcher extends SystemUI private ShortcutKeyServiceProxy mShortcutKeyServiceProxy = new ShortcutKeyServiceProxy(this); private IWindowManager mWindowManagerService = WindowManagerGlobal.getWindowManagerService(); - private IActivityManager mActivityManager = ActivityManager.getService(); protected final long META_MASK = ((long) KeyEvent.META_META_ON) << Integer.SIZE; protected final long ALT_MASK = ((long) KeyEvent.META_ALT_ON) << Integer.SIZE; @@ -109,7 +95,7 @@ public class ShortcutKeyDispatcher extends SystemUI ? DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT : DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT; List<ActivityManager.RecentTaskInfo> taskList = - SystemServicesProxy.getInstance(mContext).getRecentTasks(1, USER_CURRENT); + ActivityManagerWrapper.getInstance().getRecentTasks(1, USER_CURRENT); recents.showRecentApps( false /* triggeredFromAltTab */, false /* fromHome */); diff --git a/packages/SystemUI/tests/Android.mk b/packages/SystemUI/tests/Android.mk index 27c16d53ce78..b695919dc2b5 100644 --- a/packages/SystemUI/tests/Android.mk +++ b/packages/SystemUI/tests/Android.mk @@ -38,6 +38,7 @@ LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res \ LOCAL_STATIC_ANDROID_LIBRARIES := \ SystemUIPluginLib \ + SystemUISharedLib \ android-support-v4 \ android-support-v7-recyclerview \ android-support-v7-preference \ diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java index 47d21f8026e1..f6fcaae4f4c3 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java @@ -19,6 +19,9 @@ package com.android.server.accessibility; import android.content.Context; import android.os.Handler; import android.os.PowerManager; +import android.util.DebugUtils; +import android.util.ExceptionUtils; +import android.util.Log; import android.util.Pools.SimplePool; import android.util.Slog; import android.util.SparseBooleanArray; @@ -31,6 +34,7 @@ import android.view.MotionEvent; import android.view.WindowManagerPolicy; import android.view.accessibility.AccessibilityEvent; +import com.android.internal.util.BitUtils; import com.android.server.LocalServices; /** @@ -188,6 +192,7 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo } if (mEventHandler == null) { + if (DEBUG) Slog.d(TAG, "mEventHandler == null for event " + event); super.onInputEvent(event, policyFlags); return; } @@ -339,6 +344,8 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo MotionEvent transformedEvent = MotionEvent.obtain(event); mEventHandler.onMotionEvent(transformedEvent, event, policyFlags); transformedEvent.recycle(); + } else { + if (DEBUG) Slog.d(TAG, "mEventHandler == null for " + event); } } @@ -376,6 +383,10 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo } void setUserAndEnabledFeatures(int userId, int enabledFeatures) { + if (DEBUG) { + Slog.i(TAG, "setUserAndEnabledFeatures(userId = " + userId + ", enabledFeatures = 0x" + + Integer.toHexString(enabledFeatures) + ")"); + } if (mEnabledFeatures == enabledFeatures && mUserId == userId) { return; } @@ -402,6 +413,8 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo } private void enableFeatures() { + if (DEBUG) Slog.i(TAG, "enableFeatures()"); + resetStreamState(); if ((mEnabledFeatures & FLAG_FEATURE_AUTOCLICK) != 0) { @@ -448,7 +461,7 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo */ private void addFirstEventHandler(EventStreamTransformation handler) { if (mEventHandler != null) { - handler.setNext(mEventHandler); + handler.setNext(mEventHandler); } else { handler.setNext(this); } diff --git a/services/accessibility/java/com/android/server/accessibility/MagnificationController.java b/services/accessibility/java/com/android/server/accessibility/MagnificationController.java index 98b8e6b723ac..a10b7a20d741 100644 --- a/services/accessibility/java/com/android/server/accessibility/MagnificationController.java +++ b/services/accessibility/java/com/android/server/accessibility/MagnificationController.java @@ -56,6 +56,7 @@ import java.util.Locale; * constraints. */ class MagnificationController implements Handler.Callback { + private static final boolean DEBUG = false; private static final String LOG_TAG = "MagnificationController"; public static final float MIN_SCALE = 1.0f; @@ -509,6 +510,12 @@ class MagnificationController implements Handler.Callback { private boolean setScaleAndCenterLocked(float scale, float centerX, float centerY, boolean animate, int id) { + if (DEBUG) { + Slog.i(LOG_TAG, + "setScaleAndCenterLocked(scale = " + scale + ", centerX = " + centerX + + ", centerY = " + centerY + ", animate = " + animate + ", id = " + id + + ")"); + } final boolean changed = updateMagnificationSpecLocked(scale, centerX, centerY); sendSpecToAnimation(mCurrentMagnificationSpec, animate); if (isMagnifying() && (id != INVALID_ID)) { @@ -535,7 +542,9 @@ class MagnificationController implements Handler.Callback { final float nonNormOffsetX = mCurrentMagnificationSpec.offsetX - offsetX; final float nonNormOffsetY = mCurrentMagnificationSpec.offsetY - offsetY; - updateCurrentSpecWithOffsetsLocked(nonNormOffsetX, nonNormOffsetY); + if (updateCurrentSpecWithOffsetsLocked(nonNormOffsetX, nonNormOffsetY)) { + onMagnificationChangedLocked(); + } if (id != INVALID_ID) { mIdOfLastServiceToMagnify = id; } @@ -633,6 +642,11 @@ class MagnificationController implements Handler.Callback { } private boolean updateCurrentSpecWithOffsetsLocked(float nonNormOffsetX, float nonNormOffsetY) { + if (DEBUG) { + Slog.i(LOG_TAG, + "updateCurrentSpecWithOffsetsLocked(nonNormOffsetX = " + nonNormOffsetX + + ", nonNormOffsetY = " + nonNormOffsetY + ")"); + } boolean changed = false; final float offsetX = MathUtils.constrain(nonNormOffsetX, getMinOffsetXLocked(), 0); if (Float.compare(mCurrentMagnificationSpec.offsetX, offsetX) != 0) { @@ -750,6 +764,9 @@ class MagnificationController implements Handler.Callback { } private void sendSpecToAnimation(MagnificationSpec spec, boolean animate) { + if (DEBUG) { + Slog.i(LOG_TAG, "sendSpecToAnimation(spec = " + spec + ", animate = " + animate + ")"); + } if (Thread.currentThread().getId() == mMainThreadId) { mSpecAnimationBridge.updateSentSpecMainThread(spec, animate); } else { diff --git a/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java index 969f5b014ac8..9b2b4eb7ebee 100644 --- a/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java +++ b/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java @@ -101,21 +101,12 @@ import com.android.internal.annotations.VisibleForTesting; */ @SuppressWarnings("WeakerAccess") class MagnificationGestureHandler extends BaseEventStreamTransformation { - private static final String LOG_TAG = "MagnificationEventHandler"; + private static final String LOG_TAG = "MagnificationGestureHandler"; private static final boolean DEBUG_ALL = false; private static final boolean DEBUG_STATE_TRANSITIONS = false || DEBUG_ALL; private static final boolean DEBUG_DETECTING = false || DEBUG_ALL; - private static final boolean DEBUG_PANNING = false || DEBUG_ALL; - - /** @see DelegatingState */ - @VisibleForTesting static final int STATE_DELEGATING = 1; - /** @see DetectingState */ - @VisibleForTesting static final int STATE_DETECTING = 2; - /** @see ViewportDraggingState */ - @VisibleForTesting static final int STATE_VIEWPORT_DRAGGING = 3; - /** @see PanningScalingState */ - @VisibleForTesting static final int STATE_PANNING_SCALING = 4; + private static final boolean DEBUG_PANNING_SCALING = false || DEBUG_ALL; private static final float MIN_SCALE = 2.0f; private static final float MAX_SCALE = 5.0f; @@ -184,6 +175,8 @@ class MagnificationGestureHandler extends BaseEventStreamTransformation { @Override public void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) { + if (DEBUG_ALL) Slog.i(LOG_TAG, "onMotionEvent(" + event + ")"); + if ((!mDetectTripleTap && !mDetectShortcutTrigger) || !event.isFromSource(SOURCE_TOUCHSCREEN)) { dispatchTransformedEvent(event, rawEvent, policyFlags); @@ -213,6 +206,11 @@ class MagnificationGestureHandler extends BaseEventStreamTransformation { @Override public void onDestroy() { + if (DEBUG_STATE_TRANSITIONS) { + Slog.i(LOG_TAG, "onDestroy(); delayed = " + + MotionEventInfo.toString(mDetectingState.mDelayedEventQueue)); + } + if (mScreenStateReceiver != null) { mScreenStateReceiver.unregister(); } @@ -239,6 +237,8 @@ class MagnificationGestureHandler extends BaseEventStreamTransformation { private void dispatchTransformedEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) { + if (DEBUG_ALL) Slog.i(LOG_TAG, "dispatchTransformedEvent(event = " + event + ")"); + // If the touchscreen event is within the magnified portion of the screen we have // to change its location to be where the user thinks he is poking the // UI which may have been magnified and panned. @@ -332,8 +332,6 @@ class MagnificationGestureHandler extends BaseEventStreamTransformation { * magnification level. * This makes it the preferred mode for one-off adjustments, due to its precision and ease of * triggering. - * - * @see #STATE_PANNING_SCALING */ final class PanningScalingState extends SimpleOnGestureListener implements OnScaleGestureListener, State { @@ -385,7 +383,7 @@ class MagnificationGestureHandler extends BaseEventStreamTransformation { if (mCurrentState != mPanningScalingState) { return true; } - if (DEBUG_PANNING) { + if (DEBUG_PANNING_SCALING) { Slog.i(LOG_TAG, "Panned content by scrollX: " + distanceX + " scrollY: " + distanceY); } @@ -402,12 +400,8 @@ class MagnificationGestureHandler extends BaseEventStreamTransformation { return false; } final float deltaScale = detector.getScaleFactor() - mInitialScaleFactor; - if (abs(deltaScale) > mScalingThreshold) { - mScaling = true; - return true; - } else { - return false; - } + mScaling = abs(deltaScale) > mScalingThreshold; + return mScaling; } final float initialScale = mMagnificationController.getScale(); @@ -431,6 +425,7 @@ class MagnificationGestureHandler extends BaseEventStreamTransformation { final float pivotX = detector.getFocusX(); final float pivotY = detector.getFocusY(); + if (DEBUG_PANNING_SCALING) Slog.i(LOG_TAG, "Scaled content to: " + scale + "x"); mMagnificationController.setScale(scale, pivotX, pivotY, false, AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID); return /* handled: */ true; @@ -469,8 +464,6 @@ class MagnificationGestureHandler extends BaseEventStreamTransformation { * Unlike when {@link PanningScalingState panning}, the viewport moves in the opposite direction * of the finger, and any part of the screen is reachable without lifting the finger. * This makes it the preferable mode for tasks like reading text spanning full screen width. - * - * @see #STATE_VIEWPORT_DRAGGING */ final class ViewportDraggingState implements State { @@ -534,7 +527,7 @@ class MagnificationGestureHandler extends BaseEventStreamTransformation { final class DelegatingState implements State { /** - * Time of last {@link MotionEvent#ACTION_DOWN} while in {@link #STATE_DELEGATING} + * Time of last {@link MotionEvent#ACTION_DOWN} while in {@link DelegatingState} */ public long mLastDelegatedDownEventTime; @@ -563,8 +556,6 @@ class MagnificationGestureHandler extends BaseEventStreamTransformation { /** * This class handles motion events when the event dispatch has not yet * determined what the user is doing. It watches for various tap events. - * - * @see #STATE_DETECTING */ final class DetectingState implements State, Handler.Callback { @@ -737,14 +728,14 @@ class MagnificationGestureHandler extends BaseEventStreamTransformation { return MotionEventInfo.countOf(mDelayedEventQueue, ACTION_UP); } - /** -> {@link #STATE_DELEGATING} */ + /** -> {@link DelegatingState} */ public void afterMultiTapTimeoutTransitionToDelegatingState() { mHandler.sendEmptyMessageDelayed( MESSAGE_TRANSITION_TO_DELEGATING_STATE, mMultiTapMaxDelay); } - /** -> {@link #STATE_VIEWPORT_DRAGGING} */ + /** -> {@link ViewportDraggingState} */ public void afterLongTapTimeoutTransitionToDraggingState(MotionEvent event) { mHandler.sendMessageDelayed( mHandler.obtainMessage(MESSAGE_ON_TRIPLE_TAP_AND_HOLD, event), @@ -865,6 +856,8 @@ class MagnificationGestureHandler extends BaseEventStreamTransformation { } private void zoomOn(float centerX, float centerY) { + if (DEBUG_DETECTING) Slog.i(LOG_TAG, "zoomOn(" + centerX + ", " + centerY + ")"); + final float scale = MathUtils.constrain( mMagnificationController.getPersistedScale(), MIN_SCALE, MAX_SCALE); @@ -875,6 +868,8 @@ class MagnificationGestureHandler extends BaseEventStreamTransformation { } private void zoomOff() { + if (DEBUG_DETECTING) Slog.i(LOG_TAG, "zoomOff()"); + mMagnificationController.reset(/* animate */ true); } diff --git a/services/accessibility/java/com/android/server/accessibility/MotionEventInjector.java b/services/accessibility/java/com/android/server/accessibility/MotionEventInjector.java index 7925510c6178..b6b781290c65 100644 --- a/services/accessibility/java/com/android/server/accessibility/MotionEventInjector.java +++ b/services/accessibility/java/com/android/server/accessibility/MotionEventInjector.java @@ -36,6 +36,7 @@ import android.view.WindowManagerPolicy; import com.android.internal.os.SomeArgs; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; /** @@ -241,17 +242,24 @@ public class MotionEventInjector extends BaseEventStreamTransformation implement int continuedPointerId = mStrokeIdToPointerId .get(touchPoint.mContinuedStrokeId, -1); if (continuedPointerId == -1) { + Slog.w(LOG_TAG, "Can't continue gesture due to unknown continued stroke id in " + + touchPoint); return false; } mStrokeIdToPointerId.put(touchPoint.mStrokeId, continuedPointerId); int lastPointIndex = findPointByStrokeId( mLastTouchPoints, mNumLastTouchPoints, touchPoint.mContinuedStrokeId); if (lastPointIndex < 0) { + Slog.w(LOG_TAG, "Can't continue gesture due continued gesture id of " + + touchPoint + " not matching any previous strokes in " + + Arrays.asList(mLastTouchPoints)); return false; } if (mLastTouchPoints[lastPointIndex].mIsEndOfPath || (mLastTouchPoints[lastPointIndex].mX != touchPoint.mX) || (mLastTouchPoints[lastPointIndex].mY != touchPoint.mY)) { + Slog.w(LOG_TAG, "Can't continue gesture due to points mismatch between " + + mLastTouchPoints[lastPointIndex] + " and " + touchPoint); return false; } // Update the last touch point to match the continuation, so the gestures will diff --git a/services/core/java/com/android/server/BatteryService.java b/services/core/java/com/android/server/BatteryService.java index 6d9c977fe4c7..47be0a704d00 100644 --- a/services/core/java/com/android/server/BatteryService.java +++ b/services/core/java/com/android/server/BatteryService.java @@ -24,6 +24,7 @@ import android.os.PowerManager; import android.os.ResultReceiver; import android.os.ShellCallback; import android.os.ShellCommand; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.IBatteryStats; import com.android.internal.util.DumpUtils; import com.android.server.am.BatteryStatsService; @@ -35,7 +36,10 @@ import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; +import android.hidl.manager.V1_0.IServiceManager; +import android.hidl.manager.V1_0.IServiceNotification; import android.hardware.health.V2_0.HealthInfo; +import android.hardware.health.V2_0.IHealth; import android.os.BatteryManager; import android.os.BatteryManagerInternal; import android.os.BatteryProperties; @@ -63,6 +67,9 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintWriter; +import java.util.Arrays; +import java.util.List; +import java.util.NoSuchElementException; /** * <p>BatteryService monitors the charging status, and charge level of the device @@ -1020,4 +1027,121 @@ public final class BatteryService extends SystemService { } } } + + /** + * HealthServiceWrapper wraps the internal IHealth service and refreshes the service when + * necessary. + * + * On new registration of IHealth service, {@link #onRegistration onRegistration} is called and + * the internal service is refreshed. + * On death of an existing IHealth service, the internal service is NOT cleared to avoid + * race condition between death notification and new service notification. Hence, + * a caller must check for transaction errors when calling into the service. + * + * @hide Should only be used internally. + */ + @VisibleForTesting + static final class HealthServiceWrapper { + private static final String TAG = "HealthServiceWrapper"; + public static final String INSTANCE_HEALTHD = "backup"; + public static final String INSTANCE_VENDOR = "default"; + // All interesting instances, sorted by priority high -> low. + private static final List<String> sAllInstances = + Arrays.asList(INSTANCE_VENDOR, INSTANCE_HEALTHD); + + private final IServiceNotification mNotification = new Notification(); + private Callback mCallback; + private IHealthSupplier mHealthSupplier; + + /** + * init should be called after constructor. For testing purposes, init is not called by + * constructor. + */ + HealthServiceWrapper() { + } + + /** + * Start monitoring registration of new IHealth services. Only instances that are in + * {@code sAllInstances} and in device / framework manifest are used. This function should + * only be called once. + * @throws RemoteException transaction error when talking to IServiceManager + * @throws NoSuchElementException if one of the following cases: + * - No service manager; + * - none of {@code sAllInstances} are in manifests (i.e. not + * available on this device), or none of these instances are available to current + * process. + * @throws NullPointerException when callback is null or supplier is null + */ + void init(Callback callback, + IServiceManagerSupplier managerSupplier, + IHealthSupplier healthSupplier) + throws RemoteException, NoSuchElementException, NullPointerException { + if (callback == null || managerSupplier == null || healthSupplier == null) + throw new NullPointerException(); + + mCallback = callback; + mHealthSupplier = healthSupplier; + + IServiceManager manager = managerSupplier.get(); + for (String name : sAllInstances) { + if (manager.getTransport(IHealth.kInterfaceName, name) == + IServiceManager.Transport.EMPTY) { + continue; + } + + manager.registerForNotifications(IHealth.kInterfaceName, name, mNotification); + Slog.i(TAG, "health: HealthServiceWrapper listening to instance " + name); + return; + } + + throw new NoSuchElementException(String.format( + "No IHealth service instance among %s is available. Perhaps no permission?", + sAllInstances.toString())); + } + + interface Callback { + /** + * This function is invoked asynchronously when a new and related IServiceNotification + * is received. + * @param service the recently retrieved service from IServiceManager. + * Can be a dead service before service notification of a new service is delivered. + * Implementation must handle cases for {@link RemoteException}s when calling + * into service. + * @param instance instance name. + */ + void onRegistration(IHealth service, String instance); + } + + /** + * Supplier of services. + * Must not return null; throw {@link NoSuchElementException} if a service is not available. + */ + interface IServiceManagerSupplier { + IServiceManager get() throws NoSuchElementException, RemoteException; + } + /** + * Supplier of services. + * Must not return null; throw {@link NoSuchElementException} if a service is not available. + */ + interface IHealthSupplier { + IHealth get(String instanceName) throws NoSuchElementException, RemoteException; + } + + private class Notification extends IServiceNotification.Stub { + @Override + public final void onRegistration(String interfaceName, String instanceName, + boolean preexisting) { + if (!IHealth.kInterfaceName.equals(interfaceName)) return; + if (!sAllInstances.contains(instanceName)) return; + try { + IHealth service = mHealthSupplier.get(instanceName); + Slog.i(TAG, "health: new instance registered " + instanceName); + mCallback.onRegistration(service, instanceName); + } catch (NoSuchElementException | RemoteException ex) { + Slog.e(TAG, "health: Cannot get instance '" + instanceName + "': " + + ex.getMessage() + ". Perhaps no permission?"); + } + } + } + } } diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 5f377f7aa7a6..f17c9ac3ecf9 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -1710,7 +1710,6 @@ public class ActivityManagerService extends IActivityManager.Stub static final int PUSH_TEMP_WHITELIST_UI_MSG = 68; static final int SERVICE_FOREGROUND_CRASH_MSG = 69; static final int DISPATCH_OOM_ADJ_OBSERVER_MSG = 70; - static final int TOP_APP_KILLED_BY_LMK_MSG = 73; static final int NOTIFY_VR_KEYGUARD_MSG = 74; static final int FIRST_ACTIVITY_STACK_MSG = 100; @@ -1936,17 +1935,6 @@ public class ActivityManagerService extends IActivityManager.Stub dispatchProcessDied(pid, uid); break; } - case TOP_APP_KILLED_BY_LMK_MSG: { - final String appName = (String) msg.obj; - final AlertDialog d = new BaseErrorDialog(mUiContext); - d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ERROR); - d.setTitle(mUiContext.getText(R.string.top_app_killed_title)); - d.setMessage(mUiContext.getString(R.string.top_app_killed_message, appName)); - d.setButton(DialogInterface.BUTTON_POSITIVE, mUiContext.getText(R.string.close), - obtainMessage(DISMISS_DIALOG_UI_MSG, d)); - d.show(); - break; - } case DISPATCH_UIDS_CHANGED_UI_MSG: { dispatchUidsChanged(); } break; @@ -5424,7 +5412,6 @@ public class ActivityManagerService extends IActivityManager.Stub boolean doLowMem = app.instr == null; boolean doOomAdj = doLowMem; if (!app.killedByAm) { - maybeNotifyTopAppKilledLocked(app); Slog.i(TAG, "Process " + app.processName + " (pid " + pid + ") has died: " + ProcessList.makeOomAdjString(app.setAdj) + ProcessList.makeProcStateString(app.setProcState)); @@ -5458,25 +5445,6 @@ public class ActivityManagerService extends IActivityManager.Stub } } - /** Show system error dialog when a top app is killed by LMK */ - void maybeNotifyTopAppKilledLocked(ProcessRecord app) { - if (!shouldNotifyTopAppKilledLocked(app)) { - return; - } - - Message msg = mHandler.obtainMessage(TOP_APP_KILLED_BY_LMK_MSG); - msg.obj = mContext.getPackageManager().getApplicationLabel(app.info); - mUiHandler.sendMessage(msg); - } - - /** Only show notification when the top app is killed on low ram devices */ - private boolean shouldNotifyTopAppKilledLocked(ProcessRecord app) { - final ActivityRecord TOP_ACT = resumedAppLocked(); - final ProcessRecord TOP_APP = TOP_ACT != null ? TOP_ACT.app : null; - return app == TOP_APP && - ActivityManager.isLowRamDeviceStatic(); - } - /** * If a stack trace dump file is configured, dump process stack traces. * @param clearTraces causes the dump file to be erased prior to the new diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java index 40e568c27900..2c72a4db635a 100644 --- a/services/core/java/com/android/server/am/ActivityRecord.java +++ b/services/core/java/com/android/server/am/ActivityRecord.java @@ -423,9 +423,13 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo if (iconFilename != null || taskDescription.getLabel() != null || taskDescription.getPrimaryColor() != 0) { pw.print(prefix); pw.print("taskDescription:"); - pw.print(" iconFilename="); pw.print(taskDescription.getIconFilename()); pw.print(" label=\""); pw.print(taskDescription.getLabel()); pw.print("\""); + pw.print(" icon="); pw.print(taskDescription.getInMemoryIcon() != null + ? taskDescription.getInMemoryIcon().getByteCount() + " bytes" + : "null"); + pw.print(" iconResource="); pw.print(taskDescription.getIconResource()); + pw.print(" iconFilename="); pw.print(taskDescription.getIconFilename()); pw.print(" primaryColor="); pw.println(Integer.toHexString(taskDescription.getPrimaryColor())); pw.print(prefix + " backgroundColor="); @@ -435,9 +439,6 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo pw.print(prefix + " navigationBarColor="); pw.println(Integer.toHexString(taskDescription.getNavigationBarColor())); } - if (iconFilename == null && taskDescription.getIcon() != null) { - pw.print(prefix); pw.println("taskDescription contains Bitmap"); - } } if (results != null) { pw.print(prefix); pw.print("results="); pw.println(results); @@ -2746,6 +2747,8 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo void setShowWhenLocked(boolean showWhenLocked) { mShowWhenLocked = showWhenLocked; + mStackSupervisor.ensureActivitiesVisibleLocked(null, 0 /* configChanges */, + false /* preserveWindows */); } /** diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java index 0bc30cf4110b..a1b45a1e1757 100644 --- a/services/core/java/com/android/server/am/TaskRecord.java +++ b/services/core/java/com/android/server/am/TaskRecord.java @@ -1559,6 +1559,7 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi // values in the TaskRecord. String label = null; String iconFilename = null; + int iconResource = -1; int colorPrimary = 0; int colorBackground = 0; int statusBarColor = 0; @@ -1570,6 +1571,9 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi if (label == null) { label = r.taskDescription.getLabel(); } + if (iconResource == -1) { + iconResource = r.taskDescription.getIconResource(); + } if (iconFilename == null) { iconFilename = r.taskDescription.getIconFilename(); } @@ -1584,8 +1588,8 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi } topActivity = false; } - lastTaskDescription = new TaskDescription(label, null, iconFilename, colorPrimary, - colorBackground, statusBarColor, navigationBarColor); + lastTaskDescription = new TaskDescription(label, null, iconResource, iconFilename, + colorPrimary, colorBackground, statusBarColor, navigationBarColor); if (mWindowContainerController != null) { mWindowContainerController.setTaskDescription(lastTaskDescription); } diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java index 5a295942a84f..4aa8adb9dc78 100644 --- a/services/core/java/com/android/server/am/UserController.java +++ b/services/core/java/com/android/server/am/UserController.java @@ -67,6 +67,7 @@ import android.os.RemoteCallbackList; import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemClock; +import android.os.Trace; import android.os.UserHandle; import android.os.UserManager; import android.os.UserManagerInternal; @@ -79,6 +80,7 @@ import android.util.Pair; import android.util.Slog; import android.util.SparseArray; import android.util.SparseIntArray; +import android.util.TimingsTraceLog; import com.android.internal.R; import com.android.internal.annotations.GuardedBy; @@ -232,6 +234,7 @@ class UserController implements Handler.Callback { mUiHandler = mInjector.getUiHandler(this); // User 0 is the first and only user that runs at boot. final UserState uss = new UserState(UserHandle.SYSTEM); + uss.mUnlockProgress.addListener(new UserProgressListener()); mStartedUsers.put(UserHandle.USER_SYSTEM, uss); mUserLru.add(UserHandle.USER_SYSTEM); mLockPatternUtils = mInjector.getLockPatternUtils(); @@ -903,6 +906,7 @@ class UserController implements Handler.Callback { uss = mStartedUsers.get(userId); if (uss == null) { uss = new UserState(UserHandle.of(userId)); + uss.mUnlockProgress.addListener(new UserProgressListener()); mStartedUsers.put(userId, uss); updateStartedUserArrayLU(); needStart = true; @@ -1854,6 +1858,33 @@ class UserController implements Handler.Callback { return false; } + private static class UserProgressListener extends IProgressListener.Stub { + private volatile long mUnlockStarted; + @Override + public void onStarted(int id, Bundle extras) throws RemoteException { + Slog.d(TAG, "Started unlocking user " + id); + mUnlockStarted = SystemClock.uptimeMillis(); + } + + @Override + public void onProgress(int id, int progress, Bundle extras) throws RemoteException { + Slog.d(TAG, "Unlocking user " + id + " progress " + progress); + } + + @Override + public void onFinished(int id, Bundle extras) throws RemoteException { + long unlockTime = SystemClock.uptimeMillis() - mUnlockStarted; + + // Report system user unlock time to perf dashboard + if (id == UserHandle.USER_SYSTEM) { + new TimingsTraceLog("SystemServerTiming", Trace.TRACE_TAG_SYSTEM_SERVER) + .logDuration("SystemUserUnlock", unlockTime); + } else { + Slog.d(TAG, "Unlocking user " + id + " took " + unlockTime + " ms"); + } + } + }; + @VisibleForTesting static class Injector { private final ActivityManagerService mService; diff --git a/services/core/java/com/android/server/clipboard/ClipboardService.java b/services/core/java/com/android/server/clipboard/ClipboardService.java index e6228d46e15c..0c9d70a95ab9 100644 --- a/services/core/java/com/android/server/clipboard/ClipboardService.java +++ b/services/core/java/com/android/server/clipboard/ClipboardService.java @@ -435,11 +435,12 @@ public class ClipboardService extends SystemService { } private boolean isDeviceLocked() { + int callingUserId = UserHandle.getCallingUserId(); final long token = Binder.clearCallingIdentity(); try { final KeyguardManager keyguardManager = getContext().getSystemService( KeyguardManager.class); - return keyguardManager != null && keyguardManager.isDeviceLocked(); + return keyguardManager != null && keyguardManager.isDeviceLocked(callingUserId); } finally { Binder.restoreCallingIdentity(token); } diff --git a/services/core/java/com/android/server/job/controllers/TimeController.java b/services/core/java/com/android/server/job/controllers/TimeController.java index d90699a61928..ee4c606fa6d3 100644 --- a/services/core/java/com/android/server/job/controllers/TimeController.java +++ b/services/core/java/com/android/server/job/controllers/TimeController.java @@ -110,7 +110,7 @@ public final class TimeController extends StateController { maybeUpdateAlarmsLocked( job.hasTimingDelayConstraint() ? job.getEarliestRunTime() : Long.MAX_VALUE, job.hasDeadlineConstraint() ? job.getLatestRunTimeElapsed() : Long.MAX_VALUE, - job.getSourceUid()); + new WorkSource(job.getSourceUid(), job.getSourcePackageName())); } } @@ -156,6 +156,7 @@ public final class TimeController extends StateController { synchronized (mLock) { long nextExpiryTime = Long.MAX_VALUE; int nextExpiryUid = 0; + String nextExpiryPackageName = null; final long nowElapsedMillis = SystemClock.elapsedRealtime(); Iterator<JobStatus> it = mTrackedJobs.iterator(); @@ -171,10 +172,13 @@ public final class TimeController extends StateController { } else { // Sorted by expiry time, so take the next one and stop. nextExpiryTime = job.getLatestRunTimeElapsed(); nextExpiryUid = job.getSourceUid(); + nextExpiryPackageName = job.getSourcePackageName(); break; } } - setDeadlineExpiredAlarmLocked(nextExpiryTime, nextExpiryUid); + setDeadlineExpiredAlarmLocked(nextExpiryTime, nextExpiryPackageName != null + ? new WorkSource(nextExpiryUid, nextExpiryPackageName) + : new WorkSource(nextExpiryUid)); } } @@ -200,6 +204,7 @@ public final class TimeController extends StateController { final long nowElapsedMillis = SystemClock.elapsedRealtime(); long nextDelayTime = Long.MAX_VALUE; int nextDelayUid = 0; + String nextDelayPackageName = null; boolean ready = false; Iterator<JobStatus> it = mTrackedJobs.iterator(); while (it.hasNext()) { @@ -221,13 +226,16 @@ public final class TimeController extends StateController { if (nextDelayTime > jobDelayTime) { nextDelayTime = jobDelayTime; nextDelayUid = job.getSourceUid(); + nextDelayPackageName = job.getSourcePackageName(); } } } if (ready) { mStateChangedListener.onControllerStateChanged(); } - setDelayExpiredAlarmLocked(nextDelayTime, nextDelayUid); + setDelayExpiredAlarmLocked(nextDelayTime, nextDelayPackageName != null + ? new WorkSource(nextDelayUid, nextDelayPackageName) + : new WorkSource(nextDelayUid)); } } @@ -241,12 +249,12 @@ public final class TimeController extends StateController { } private void maybeUpdateAlarmsLocked(long delayExpiredElapsed, long deadlineExpiredElapsed, - int uid) { + WorkSource ws) { if (delayExpiredElapsed < mNextDelayExpiredElapsedMillis) { - setDelayExpiredAlarmLocked(delayExpiredElapsed, uid); + setDelayExpiredAlarmLocked(delayExpiredElapsed, ws); } if (deadlineExpiredElapsed < mNextJobExpiredElapsedMillis) { - setDeadlineExpiredAlarmLocked(deadlineExpiredElapsed, uid); + setDeadlineExpiredAlarmLocked(deadlineExpiredElapsed, ws); } } @@ -255,11 +263,11 @@ public final class TimeController extends StateController { * delay will expire. * This alarm <b>will</b> wake up the phone. */ - private void setDelayExpiredAlarmLocked(long alarmTimeElapsedMillis, int uid) { + private void setDelayExpiredAlarmLocked(long alarmTimeElapsedMillis, WorkSource ws) { alarmTimeElapsedMillis = maybeAdjustAlarmTime(alarmTimeElapsedMillis); mNextDelayExpiredElapsedMillis = alarmTimeElapsedMillis; updateAlarmWithListenerLocked(DELAY_TAG, mNextDelayExpiredListener, - mNextDelayExpiredElapsedMillis, uid); + mNextDelayExpiredElapsedMillis, ws); } /** @@ -267,11 +275,11 @@ public final class TimeController extends StateController { * deadline will expire. * This alarm <b>will</b> wake up the phone. */ - private void setDeadlineExpiredAlarmLocked(long alarmTimeElapsedMillis, int uid) { + private void setDeadlineExpiredAlarmLocked(long alarmTimeElapsedMillis, WorkSource ws) { alarmTimeElapsedMillis = maybeAdjustAlarmTime(alarmTimeElapsedMillis); mNextJobExpiredElapsedMillis = alarmTimeElapsedMillis; updateAlarmWithListenerLocked(DEADLINE_TAG, mDeadlineExpiredListener, - mNextJobExpiredElapsedMillis, uid); + mNextJobExpiredElapsedMillis, ws); } private long maybeAdjustAlarmTime(long proposedAlarmTimeElapsedMillis) { @@ -283,7 +291,7 @@ public final class TimeController extends StateController { } private void updateAlarmWithListenerLocked(String tag, OnAlarmListener listener, - long alarmTimeElapsed, int uid) { + long alarmTimeElapsed, WorkSource ws) { ensureAlarmServiceLocked(); if (alarmTimeElapsed == Long.MAX_VALUE) { mAlarmService.cancel(listener); @@ -292,7 +300,7 @@ public final class TimeController extends StateController { Slog.d(TAG, "Setting " + tag + " for: " + alarmTimeElapsed); } mAlarmService.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, alarmTimeElapsed, - AlarmManager.WINDOW_HEURISTIC, 0, tag, listener, null, new WorkSource(uid)); + AlarmManager.WINDOW_HEURISTIC, 0, tag, listener, null, ws); } } diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java index 710684f4351d..f61cec9713c0 100644 --- a/services/core/java/com/android/server/notification/ZenModeHelper.java +++ b/services/core/java/com/android/server/notification/ZenModeHelper.java @@ -126,12 +126,6 @@ public class ZenModeHelper { mAppOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE); mDefaultConfig = new ZenModeConfig(); - mDefaultRuleWeeknightsName = mContext.getResources() - .getString(R.string.zen_mode_default_weeknights_name); - mDefaultRuleWeekendsName = mContext.getResources() - .getString(R.string.zen_mode_default_weekends_name); - mDefaultRuleEventsName = mContext.getResources() - .getString(R.string.zen_mode_default_events_name); setDefaultZenRules(mContext); mConfig = mDefaultConfig; mConfigs.put(UserHandle.USER_SYSTEM, mConfig); @@ -436,6 +430,7 @@ public class ZenModeHelper { } private void appendDefaultRules (ZenModeConfig config) { + getDefaultRuleNames(); appendDefaultScheduleRules(config); appendDefaultEventRules(config); } @@ -815,6 +810,16 @@ public class ZenModeHelper { } } + private void getDefaultRuleNames() { + // on locale-change, these values differ + mDefaultRuleWeeknightsName = mContext.getResources() + .getString(R.string.zen_mode_default_weeknights_name); + mDefaultRuleWeekendsName = mContext.getResources() + .getString(R.string.zen_mode_default_weekends_name); + mDefaultRuleEventsName = mContext.getResources() + .getString(R.string.zen_mode_default_events_name); + } + @VisibleForTesting protected void applyRestrictions() { final boolean zen = mZenMode != Global.ZEN_MODE_OFF; @@ -928,6 +933,7 @@ public class ZenModeHelper { weeknights.days = ZenModeConfig.WEEKNIGHT_DAYS; weeknights.startHour = 22; weeknights.endHour = 7; + weeknights.exitAtAlarm = true; final ZenRule rule1 = new ZenRule(); rule1.enabled = false; rule1.name = mDefaultRuleWeeknightsName; @@ -943,6 +949,7 @@ public class ZenModeHelper { weekends.startHour = 23; weekends.startMinute = 30; weekends.endHour = 10; + weekends.exitAtAlarm = true; final ZenRule rule2 = new ZenRule(); rule2.enabled = false; rule2.name = mDefaultRuleWeekendsName; diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java index d62f0934669d..496ebc2320d6 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerSession.java +++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java @@ -1127,15 +1127,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { mResolvedInstructionSets.add(archSubDir.getName()); List<File> oatFiles = Arrays.asList(archSubDir.listFiles()); - - // Only add compiled files associated with the base. - // Once b/62269291 is resolved, we can add all compiled files again. - for (File oatFile : oatFiles) { - if (oatFile.getName().equals("base.art") - || oatFile.getName().equals("base.odex") - || oatFile.getName().equals("base.vdex")) { - mResolvedInheritedFiles.add(oatFile); - } + if (!oatFiles.isEmpty()) { + mResolvedInheritedFiles.addAll(oatFiles); } } } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index d2f242695891..275db1fa543d 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -3261,23 +3261,6 @@ public class PackageManagerService extends IPackageManager.Stub return null; } - // If we have a profile for a compressed APK, copy it to the reference location. - // Since the package is the stub one, remove the stub suffix to get the normal package and - // APK name. - File profileFile = new File(getPrebuildProfilePath(pkg).replace(STUB_SUFFIX, "")); - if (profileFile.exists()) { - try { - // We could also do this lazily before calling dexopt in - // PackageDexOptimizer to prevent this happening on first boot. The issue - // is that we don't have a good way to say "do this only once". - if (!mInstaller.copySystemProfile(profileFile.getAbsolutePath(), - pkg.applicationInfo.uid, pkg.packageName)) { - Log.e(TAG, "decompressPackage failed to copy system profile!"); - } - } catch (Exception e) { - Log.e(TAG, "Failed to copy profile " + profileFile.getAbsolutePath() + " ", e); - } - } return dstCodePath; } @@ -9114,10 +9097,30 @@ public class PackageManagerService extends IPackageManager.Stub // package and APK names. String systemProfilePath = getPrebuildProfilePath(disabledPs.pkg).replace(STUB_SUFFIX, ""); - File systemProfile = new File(systemProfilePath); - // Use the profile for compilation if there exists one for the same package - // in the system partition. - useProfileForDexopt = systemProfile.exists(); + profileFile = new File(systemProfilePath); + // If we have a profile for a compressed APK, copy it to the reference + // location. + // Note that copying the profile here will cause it to override the + // reference profile every OTA even though the existing reference profile + // may have more data. We can't copy during decompression since the + // directories are not set up at that point. + if (profileFile.exists()) { + try { + // We could also do this lazily before calling dexopt in + // PackageDexOptimizer to prevent this happening on first boot. The + // issue is that we don't have a good way to say "do this only + // once". + if (!mInstaller.copySystemProfile(profileFile.getAbsolutePath(), + pkg.applicationInfo.uid, pkg.packageName)) { + Log.e(TAG, "Failed to copy system profile for stub package!"); + } else { + useProfileForDexopt = true; + } + } catch (Exception e) { + Log.e(TAG, "Failed to copy profile " + + profileFile.getAbsolutePath() + " ", e); + } + } } } } @@ -21857,11 +21860,7 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); synchronized (mAvailableFeatures) { final int count = mAvailableFeatures.size(); for (int i = 0; i < count; i++) { - final FeatureInfo feat = mAvailableFeatures.valueAt(i); - final long featureToken = proto.start(PackageServiceDumpProto.FEATURES); - proto.write(PackageServiceDumpProto.FeatureProto.NAME, feat.name); - proto.write(PackageServiceDumpProto.FeatureProto.VERSION, feat.version); - proto.end(featureToken); + mAvailableFeatures.valueAt(i).writeToProto(proto, PackageServiceDumpProto.FEATURES); } } } diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index 56595c947ed4..191b43a66d51 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -4929,11 +4929,7 @@ public final class Settings { void dumpSharedUsersProto(ProtoOutputStream proto) { final int count = mSharedUsers.size(); for (int i = 0; i < count; i++) { - final SharedUserSetting su = mSharedUsers.valueAt(i); - final long sharedUserToken = proto.start(PackageServiceDumpProto.SHARED_USERS); - proto.write(PackageServiceDumpProto.SharedUserProto.USER_ID, su.userId); - proto.write(PackageServiceDumpProto.SharedUserProto.NAME, su.name); - proto.end(sharedUserToken); + mSharedUsers.valueAt(i).writeToProto(proto, PackageServiceDumpProto.SHARED_USERS); } } diff --git a/services/core/java/com/android/server/pm/SharedUserSetting.java b/services/core/java/com/android/server/pm/SharedUserSetting.java index a0dadae37624..877da144730f 100644 --- a/services/core/java/com/android/server/pm/SharedUserSetting.java +++ b/services/core/java/com/android/server/pm/SharedUserSetting.java @@ -18,7 +18,9 @@ package com.android.server.pm; import android.annotation.Nullable; import android.content.pm.PackageParser; +import android.service.pm.PackageServiceDumpProto; import android.util.ArraySet; +import android.util.proto.ProtoOutputStream; import java.util.ArrayList; import java.util.Collection; @@ -53,6 +55,13 @@ public final class SharedUserSetting extends SettingBase { + name + "/" + userId + "}"; } + public void writeToProto(ProtoOutputStream proto, long fieldId) { + long token = proto.start(fieldId); + proto.write(PackageServiceDumpProto.SharedUserProto.USER_ID, userId); + proto.write(PackageServiceDumpProto.SharedUserProto.NAME, name); + proto.end(token); + } + void removePackage(PackageSetting packageSetting) { if (packages.remove(packageSetting)) { // recalculate the pkgFlags for this shared user if needed 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 062aa3310c21..d2d857caa240 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java @@ -417,8 +417,8 @@ public class PermissionManagerService { bp = new BasePermission(info.name, tree.getSourcePackageName(), BasePermission.TYPE_DYNAMIC); } else if (bp.isDynamic()) { - throw new SecurityException( - "Not allowed to modify non-dynamic permission " + // TODO: switch this back to SecurityException + Slog.wtf(TAG, "Not allowed to modify non-dynamic permission " + info.name); } changed = bp.addToTree(fixedLevel, info, tree); @@ -444,8 +444,8 @@ public class PermissionManagerService { return; } if (bp.isDynamic()) { - throw new SecurityException( - "Not allowed to modify non-dynamic permission " + // TODO: switch this back to SecurityException + Slog.wtf(TAG, "Not allowed to modify non-dynamic permission " + permName); } mSettings.removePermissionLocked(permName); diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java index af1fa2fe0291..fa33fe8fa92a 100644 --- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java +++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java @@ -449,6 +449,9 @@ class WindowSurfacePlacer { // animating? wtoken.setVisibility(animLp, false, transit, false, voiceInteraction); wtoken.updateReportedVisibilityLocked(); + // setAllAppWinAnimators so the windows get onExitAnimationDone once the animation is + // done. + wtoken.setAllAppWinAnimators(); // Force the allDrawn flag, because we want to start // this guy's animations regardless of whether it's // gotten drawn. diff --git a/services/tests/servicestests/Android.mk b/services/tests/servicestests/Android.mk index 6d7d4cbcfd5a..8e41a554b9e3 100644 --- a/services/tests/servicestests/Android.mk +++ b/services/tests/servicestests/Android.mk @@ -33,7 +33,10 @@ LOCAL_SRC_FILES += aidl/com/android/servicestests/aidl/INetworkStateObserver.aid aidl/com/android/servicestests/aidl/ICmdReceiverService.aidl LOCAL_SRC_FILES += $(call all-java-files-under, test-apps/JobTestApp/src) -LOCAL_JAVA_LIBRARIES := android.test.mock legacy-android-test +LOCAL_JAVA_LIBRARIES := \ + android.hidl.manager-V1.0-java \ + android.test.mock \ + legacy-android-test \ LOCAL_PACKAGE_NAME := FrameworksServicesTests LOCAL_COMPATIBILITY_SUITE := device-tests diff --git a/services/tests/servicestests/src/com/android/server/BatteryServiceTest.java b/services/tests/servicestests/src/com/android/server/BatteryServiceTest.java new file mode 100644 index 000000000000..5d1d07bf42a2 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/BatteryServiceTest.java @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server; + +import static junit.framework.Assert.*; +import static org.mockito.Mockito.*; + +import android.hardware.health.V2_0.IHealth; +import android.hidl.manager.V1_0.IServiceManager; +import android.hidl.manager.V1_0.IServiceNotification; +import android.os.RemoteException; +import android.support.test.filters.SmallTest; +import android.test.AndroidTestCase; +import android.util.Slog; + +import java.util.Arrays; +import java.util.Collection; +import java.util.NoSuchElementException; + +import org.junit.runner.RunWith; +import org.mockito.ArgumentMatcher; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.mockito.Spy; + + +public class BatteryServiceTest extends AndroidTestCase { + + @Mock IServiceManager mMockedManager; + @Mock IHealth mMockedHal; + + @Mock BatteryService.HealthServiceWrapper.Callback mCallback; + @Mock BatteryService.HealthServiceWrapper.IServiceManagerSupplier mManagerSupplier; + @Mock BatteryService.HealthServiceWrapper.IHealthSupplier mHealthServiceSupplier; + BatteryService.HealthServiceWrapper mWrapper; + + private static final String HEALTHD = BatteryService.HealthServiceWrapper.INSTANCE_HEALTHD; + private static final String VENDOR = BatteryService.HealthServiceWrapper.INSTANCE_VENDOR; + + @Override + public void setUp() { + MockitoAnnotations.initMocks(this); + } + + public static <T> ArgumentMatcher<T> isOneOf(Collection<T> collection) { + return new ArgumentMatcher<T>() { + @Override public boolean matches(T e) { + return collection.contains(e); + } + @Override public String toString() { + return collection.toString(); + } + }; + } + + private void initForInstances(String... instanceNamesArr) throws Exception { + final Collection<String> instanceNames = Arrays.asList(instanceNamesArr); + doAnswer((invocation) -> { + Slog.e("BatteryServiceTest", "health: onRegistration " + invocation.getArguments()[2]); + ((IServiceNotification)invocation.getArguments()[2]).onRegistration( + IHealth.kInterfaceName, + (String)invocation.getArguments()[1], + true /* preexisting */); + return null; + }).when(mMockedManager).registerForNotifications( + eq(IHealth.kInterfaceName), + argThat(isOneOf(instanceNames)), + any(IServiceNotification.class)); + + doReturn(mMockedHal).when(mMockedManager) + .get(eq(IHealth.kInterfaceName), argThat(isOneOf(instanceNames))); + + doReturn(IServiceManager.Transport.HWBINDER).when(mMockedManager) + .getTransport(eq(IHealth.kInterfaceName), argThat(isOneOf(instanceNames))); + + doReturn(mMockedManager).when(mManagerSupplier).get(); + doReturn(mMockedHal).when(mHealthServiceSupplier) + .get(argThat(isOneOf(instanceNames))); + + mWrapper = new BatteryService.HealthServiceWrapper(); + } + + @SmallTest + public void testWrapPreferVendor() throws Exception { + initForInstances(VENDOR, HEALTHD); + mWrapper.init(mCallback, mManagerSupplier, mHealthServiceSupplier); + verify(mCallback).onRegistration(same(mMockedHal), eq(VENDOR)); + } + + @SmallTest + public void testUseHealthd() throws Exception { + initForInstances(HEALTHD); + mWrapper.init(mCallback, mManagerSupplier, mHealthServiceSupplier); + verify(mCallback).onRegistration(same(mMockedHal), eq(HEALTHD)); + } + + @SmallTest + public void testNoService() throws Exception { + initForInstances("unrelated"); + try { + mWrapper.init(mCallback, mManagerSupplier, mHealthServiceSupplier); + fail("Expect NoSuchElementException"); + } catch (NoSuchElementException ex) { + // expected + } + } +} diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index 54f7363aff8a..de980b2ffec2 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -1592,6 +1592,18 @@ public class CarrierConfigManager { public static final String KEY_DISABLE_CHARGE_INDICATION_BOOL = "disable_charge_indication_bool"; + /** + * Boolean indicating whether to skip the call forwarding (CF) fail-to-disable dialog. + * The logic used to determine whether we succeeded in disabling is carrier specific, + * so the dialog may not always be accurate. + * {@code false} - show CF fail-to-disable dialog. + * {@code true} - skip showing CF fail-to-disable dialog. + * + * @hide + */ + public static final String KEY_SKIP_CF_FAIL_TO_DISABLE_DIALOG_BOOL = + "skip_cf_fail_to_disable_dialog_bool"; + /** The default value for every variable. */ private final static PersistableBundle sDefaults; @@ -1746,6 +1758,7 @@ public class CarrierConfigManager { sDefaults.putString(KEY_CARRIER_NAME_STRING, ""); sDefaults.putBoolean(KEY_SUPPORT_DIRECT_FDN_DIALING_BOOL, false); sDefaults.putBoolean(KEY_CARRIER_DEFAULT_DATA_ROAMING_ENABLED_BOOL, false); + sDefaults.putBoolean(KEY_SKIP_CF_FAIL_TO_DISABLE_DIALOG_BOOL, false); // MMS defaults sDefaults.putBoolean(KEY_MMS_ALIAS_ENABLED_BOOL, false); diff --git a/telephony/java/android/telephony/NetworkScanRequest.java b/telephony/java/android/telephony/NetworkScanRequest.java index d2aef2007044..9674c9300602 100644 --- a/telephony/java/android/telephony/NetworkScanRequest.java +++ b/telephony/java/android/telephony/NetworkScanRequest.java @@ -19,6 +19,7 @@ package android.telephony; import android.os.Parcel; import android.os.Parcelable; +import java.util.ArrayList; import java.util.Arrays; /** @@ -38,6 +39,20 @@ public final class NetworkScanRequest implements Parcelable { public static final int MAX_BANDS = 8; /** @hide */ public static final int MAX_CHANNELS = 32; + /** @hide */ + public static final int MAX_MCC_MNC_LIST_SIZE = 20; + /** @hide */ + public static final int MIN_SEARCH_PERIODICITY_SEC = 5; + /** @hide */ + public static final int MAX_SEARCH_PERIODICITY_SEC = 300; + /** @hide */ + public static final int MIN_SEARCH_MAX_SEC = 60; + /** @hide */ + public static final int MAX_SEARCH_MAX_SEC = 3600; + /** @hide */ + public static final int MIN_INCREMENTAL_PERIODICITY_SEC = 1; + /** @hide */ + public static final int MAX_INCREMENTAL_PERIODICITY_SEC = 10; /** Performs the scan only once */ public static final int SCAN_TYPE_ONE_SHOT = 0; @@ -46,24 +61,84 @@ public final class NetworkScanRequest implements Parcelable { * * The modem will start new scans periodically, and the interval between two scans is usually * multiple minutes. - * */ + */ public static final int SCAN_TYPE_PERIODIC = 1; /** Defines the type of the scan. */ public int scanType; + /** + * Search periodicity (in seconds). + * Expected range for the input is [5s - 300s] + * This value must be less than or equal to maxSearchTime + */ + public int searchPeriodicity; + + /** + * Maximum duration of the periodic search (in seconds). + * Expected range for the input is [60s - 3600s] + * If the search lasts this long, it will be terminated. + */ + public int maxSearchTime; + + /** + * Indicates whether the modem should report incremental + * results of the network scan to the client. + * FALSE – Incremental results are not reported. + * TRUE (default) – Incremental results are reported + */ + public boolean incrementalResults; + + /** + * Indicates the periodicity with which the modem should + * report incremental results to the client (in seconds). + * Expected range for the input is [1s - 10s] + * This value must be less than or equal to maxSearchTime + */ + public int incrementalResultsPeriodicity; + /** Describes the radio access technologies with bands or channels that need to be scanned. */ public RadioAccessSpecifier[] specifiers; /** + * Describes the List of PLMN ids (MCC-MNC) + * If any PLMN of this list is found, search should end at that point and + * results with all PLMN found till that point should be sent as response. + * If list not sent, search to be completed till end and all PLMNs found to be reported. + * Max size of array is MAX_MCC_MNC_LIST_SIZE + */ + public ArrayList<String> mccMncs; + + /** * Creates a new NetworkScanRequest with scanType and network specifiers * * @param scanType The type of the scan * @param specifiers the radio network with bands / channels to be scanned + * @param searchPeriodicity Search periodicity (in seconds) + * @param maxSearchTime Maximum duration of the periodic search (in seconds) + * @param incrementalResults Indicates whether the modem should report incremental + * results of the network scan to the client + * @param incrementalResultsPeriodicity Indicates the periodicity with which the modem should + * report incremental results to the client (in seconds) + * @param mccMncs Describes the List of PLMN ids (MCC-MNC) */ - public NetworkScanRequest(int scanType, RadioAccessSpecifier[] specifiers) { + public NetworkScanRequest(int scanType, RadioAccessSpecifier[] specifiers, + int searchPeriodicity, + int maxSearchTime, + boolean incrementalResults, + int incrementalResultsPeriodicity, + ArrayList<String> mccMncs) { this.scanType = scanType; this.specifiers = specifiers; + this.searchPeriodicity = searchPeriodicity; + this.maxSearchTime = maxSearchTime; + this.incrementalResults = incrementalResults; + this.incrementalResultsPeriodicity = incrementalResultsPeriodicity; + if (mccMncs != null) { + this.mccMncs = mccMncs; + } else { + this.mccMncs = new ArrayList<>(); + } } @Override @@ -75,6 +150,11 @@ public final class NetworkScanRequest implements Parcelable { public void writeToParcel(Parcel dest, int flags) { dest.writeInt(scanType); dest.writeParcelableArray(specifiers, flags); + dest.writeInt(searchPeriodicity); + dest.writeInt(maxSearchTime); + dest.writeBoolean(incrementalResults); + dest.writeInt(incrementalResultsPeriodicity); + dest.writeStringList(mccMncs); } private NetworkScanRequest(Parcel in) { @@ -82,6 +162,12 @@ public final class NetworkScanRequest implements Parcelable { specifiers = (RadioAccessSpecifier[]) in.readParcelableArray( Object.class.getClassLoader(), RadioAccessSpecifier.class); + searchPeriodicity = in.readInt(); + maxSearchTime = in.readInt(); + incrementalResults = in.readBoolean(); + incrementalResultsPeriodicity = in.readInt(); + mccMncs = new ArrayList<>(); + in.readStringList(mccMncs); } @Override @@ -99,13 +185,24 @@ public final class NetworkScanRequest implements Parcelable { } return (scanType == nsr.scanType - && Arrays.equals(specifiers, nsr.specifiers)); + && Arrays.equals(specifiers, nsr.specifiers) + && searchPeriodicity == nsr.searchPeriodicity + && maxSearchTime == nsr.maxSearchTime + && incrementalResults == nsr.incrementalResults + && incrementalResultsPeriodicity == nsr.incrementalResultsPeriodicity + && (((mccMncs != null) + && mccMncs.equals(nsr.mccMncs)))); } @Override public int hashCode () { return ((scanType * 31) - + (Arrays.hashCode(specifiers)) * 37); + + (Arrays.hashCode(specifiers)) * 37 + + (searchPeriodicity * 41) + + (maxSearchTime * 43) + + ((incrementalResults == true? 1 : 0) * 47) + + (incrementalResultsPeriodicity * 53) + + (mccMncs.hashCode() * 59)); } public static final Creator<NetworkScanRequest> CREATOR = diff --git a/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java b/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java index 2f85a1df8a22..c3b2c482049b 100644 --- a/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java +++ b/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java @@ -113,6 +113,10 @@ public class MbmsDownloadServiceBase extends IMbmsDownloadService.Stub { @Override public final int initialize(final int subscriptionId, final IMbmsDownloadSessionCallback callback) throws RemoteException { + if (callback == null) { + throw new NullPointerException("Callback must not be null"); + } + final int uid = Binder.getCallingUid(); callback.asBinder().linkToDeath(new DeathRecipient() { @Override @@ -240,6 +244,13 @@ public class MbmsDownloadServiceBase extends IMbmsDownloadService.Stub { public final int registerStateCallback(final DownloadRequest downloadRequest, final IDownloadStateCallback callback, int flags) throws RemoteException { final int uid = Binder.getCallingUid(); + if (downloadRequest == null) { + throw new NullPointerException("Download request must not be null"); + } + if (callback == null) { + throw new NullPointerException("Callback must not be null"); + } + DeathRecipient deathRecipient = new DeathRecipient() { @Override public void binderDied() { @@ -292,6 +303,13 @@ public class MbmsDownloadServiceBase extends IMbmsDownloadService.Stub { public final int unregisterStateCallback( final DownloadRequest downloadRequest, final IDownloadStateCallback callback) throws RemoteException { + if (downloadRequest == null) { + throw new NullPointerException("Download request must not be null"); + } + if (callback == null) { + throw new NullPointerException("Callback must not be null"); + } + DeathRecipient deathRecipient = mDownloadCallbackDeathRecipients.remove(callback.asBinder()); if (deathRecipient == null) { diff --git a/telephony/java/android/telephony/mbms/vendor/MbmsStreamingServiceBase.java b/telephony/java/android/telephony/mbms/vendor/MbmsStreamingServiceBase.java index f8f370a5fe8d..65b726dfb45d 100644 --- a/telephony/java/android/telephony/mbms/vendor/MbmsStreamingServiceBase.java +++ b/telephony/java/android/telephony/mbms/vendor/MbmsStreamingServiceBase.java @@ -65,6 +65,10 @@ public class MbmsStreamingServiceBase extends IMbmsStreamingService.Stub { @Override public final int initialize(final IMbmsStreamingSessionCallback callback, final int subscriptionId) throws RemoteException { + if (callback == null) { + throw new NullPointerException("Callback must not be null"); + } + final int uid = Binder.getCallingUid(); callback.asBinder().linkToDeath(new DeathRecipient() { @Override @@ -152,6 +156,10 @@ public class MbmsStreamingServiceBase extends IMbmsStreamingService.Stub { @Override public int startStreaming(final int subscriptionId, String serviceId, final IStreamingServiceCallback callback) throws RemoteException { + if (callback == null) { + throw new NullPointerException("Callback must not be null"); + } + final int uid = Binder.getCallingUid(); callback.asBinder().linkToDeath(new DeathRecipient() { @Override diff --git a/tests/FeatureSplit/base/Android.mk b/tests/FeatureSplit/base/Android.mk index 93f6d7a5f52b..6da1b38773ed 100644 --- a/tests/FeatureSplit/base/Android.mk +++ b/tests/FeatureSplit/base/Android.mk @@ -17,6 +17,7 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) +LOCAL_USE_AAPT2 := true LOCAL_SRC_FILES := $(call all-subdir-java-files) LOCAL_PACKAGE_NAME := FeatureSplitBase LOCAL_EXPORT_PACKAGE_RESOURCES := true diff --git a/tests/FeatureSplit/feature1/Android.mk b/tests/FeatureSplit/feature1/Android.mk index e6ba5c2d04c9..b3ea97b03d5d 100644 --- a/tests/FeatureSplit/feature1/Android.mk +++ b/tests/FeatureSplit/feature1/Android.mk @@ -26,6 +26,6 @@ LOCAL_APK_LIBRARIES := FeatureSplitBase LOCAL_RES_LIBRARIES := FeatureSplitBase LOCAL_AAPT_FLAGS += --package-id 0x80 -LOCAL_AAPT_FLAGS += --custom-package com.android.test.split.feature.one +LOCAL_AAPT_FLAGS += --rename-manifest-package com.android.test.split.feature include $(BUILD_PACKAGE) diff --git a/tests/FeatureSplit/feature1/AndroidManifest.xml b/tests/FeatureSplit/feature1/AndroidManifest.xml index b87361faac62..4e7d15102a6e 100644 --- a/tests/FeatureSplit/feature1/AndroidManifest.xml +++ b/tests/FeatureSplit/feature1/AndroidManifest.xml @@ -15,13 +15,13 @@ --> <manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.test.split.feature" + package="com.android.test.split.feature.one" featureSplit="feature1"> <uses-sdk android:minSdkVersion="21" /> <application> - <activity android:name=".one.One" android:label="Feature One"> + <activity android:name=".One" android:label="Feature One"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> diff --git a/tests/FeatureSplit/feature1/res/values/values.xml b/tests/FeatureSplit/feature1/res/values/values.xml index 10dbd9733889..0e3e73c32480 100644 --- a/tests/FeatureSplit/feature1/res/values/values.xml +++ b/tests/FeatureSplit/feature1/res/values/values.xml @@ -20,7 +20,7 @@ <integer name="test_integer2">200</integer> <color name="test_color2">#00ff00</color> <string-array name="string_array2"> - <item>@*string/app_title</item> + <item>@*com.android.test.split.feature:string/app_title</item> </string-array> </resources> diff --git a/tests/FeatureSplit/feature2/Android.mk b/tests/FeatureSplit/feature2/Android.mk index c8e860942fa3..e2fd90384538 100644 --- a/tests/FeatureSplit/feature2/Android.mk +++ b/tests/FeatureSplit/feature2/Android.mk @@ -26,6 +26,6 @@ LOCAL_APK_LIBRARIES := FeatureSplitBase LOCAL_RES_LIBRARIES := FeatureSplitBase LOCAL_AAPT_FLAGS += --package-id 0x81 -LOCAL_AAPT_FLAGS += --custom-package com.android.test.split.feature.two +LOCAL_AAPT_FLAGS += --rename-manifest-package com.android.test.split.feature include $(BUILD_PACKAGE) diff --git a/tests/FeatureSplit/feature2/AndroidManifest.xml b/tests/FeatureSplit/feature2/AndroidManifest.xml index abd0b5eb6933..bfe6f38201bd 100644 --- a/tests/FeatureSplit/feature2/AndroidManifest.xml +++ b/tests/FeatureSplit/feature2/AndroidManifest.xml @@ -15,7 +15,7 @@ --> <manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.test.split.feature" + package="com.android.test.split.feature.two" featureSplit="feature2"> <uses-sdk android:minSdkVersion="21" /> diff --git a/tests/FeatureSplit/feature2/res/values/values.xml b/tests/FeatureSplit/feature2/res/values/values.xml index af5ed1b79b26..2fa6f907b8ab 100644 --- a/tests/FeatureSplit/feature2/res/values/values.xml +++ b/tests/FeatureSplit/feature2/res/values/values.xml @@ -18,7 +18,7 @@ <integer name="test_integer3">300</integer> <color name="test_color3">#0000ff</color> <string-array name="string_array3"> - <item>@string/app_title</item> + <item>@*com.android.test.split.feature:string/app_title</item> </string-array> </resources> diff --git a/tools/bit/command.cpp b/tools/bit/command.cpp index 1ff7c221b26e..f95ea117a96e 100644 --- a/tools/bit/command.cpp +++ b/tools/bit/command.cpp @@ -189,7 +189,7 @@ run_command(const Command& command) int exec_with_path_search(const char* prog, char const* const* argv, char const* const* envp) { - if (prog[0] == '/') { + if (strchr(prog, '/') != NULL) { return execve(prog, (char*const*)argv, (char*const*)envp); } else { char* pathEnv = strdup(getenv("PATH")); diff --git a/tools/bit/main.cpp b/tools/bit/main.cpp index a8a4cfcab351..a71cea1c44f9 100644 --- a/tools/bit/main.cpp +++ b/tools/bit/main.cpp @@ -623,12 +623,13 @@ run_phases(vector<Target*> targets, const Options& options) const string buildProduct = get_required_env("TARGET_PRODUCT", false); const string buildVariant = get_required_env("TARGET_BUILD_VARIANT", false); const string buildType = get_required_env("TARGET_BUILD_TYPE", false); - const string buildDevice = get_build_var(buildTop, "TARGET_DEVICE", false); - const string buildId = get_build_var(buildTop, "BUILD_ID", false); - const string buildOut = get_out_dir(); chdir_or_exit(buildTop.c_str()); + const string buildDevice = get_build_var("TARGET_DEVICE", false); + const string buildId = get_build_var("BUILD_ID", false); + const string buildOut = get_out_dir(); + // Get the modules for the targets map<string,Module> modules; read_modules(buildOut, buildDevice, &modules, false); diff --git a/tools/bit/make.cpp b/tools/bit/make.cpp index b2ee99c2c74c..5a9ab22719cb 100644 --- a/tools/bit/make.cpp +++ b/tools/bit/make.cpp @@ -36,31 +36,16 @@ using namespace std; map<string,string> g_buildVars; -static unsigned int -get_thread_count() -{ - unsigned int threads = std::thread::hardware_concurrency(); - // Guess if the value cannot be computed - return threads == 0 ? 4 : static_cast<unsigned int>(threads * 1.3f); -} - string -get_build_var(const string& buildTop, const string& name, bool quiet) +get_build_var(const string& name, bool quiet) { int err; map<string,string>::iterator it = g_buildVars.find(name); if (it == g_buildVars.end()) { - Command cmd("make"); - cmd.AddArg("--no-print-directory"); - cmd.AddArg(string("-j") + std::to_string(get_thread_count())); - cmd.AddArg("-C"); - cmd.AddArg(buildTop); - cmd.AddArg("-f"); - cmd.AddArg("build/core/config.mk"); - cmd.AddArg(string("dumpvar-") + name); - cmd.AddEnv("CALLED_FROM_SETUP", "true"); - cmd.AddEnv("BUILD_SYSTEM", "build/core"); + Command cmd("build/soong/soong_ui.bash"); + cmd.AddArg("--dumpvar-mode"); + cmd.AddArg(name); string output = trim(get_command_output(cmd, &err, quiet)); if (err == 0) { @@ -208,10 +193,8 @@ read_modules(const string& buildOut, const string& device, map<string,Module>* r int build_goals(const vector<string>& goals) { - Command cmd("make"); - cmd.AddArg(string("-j") + std::to_string(get_thread_count())); - cmd.AddArg("-f"); - cmd.AddArg("build/core/main.mk"); + Command cmd("build/soong/soong_ui.bash"); + cmd.AddArg("--make-mode"); for (size_t i=0; i<goals.size(); i++) { cmd.AddArg(goals[i]); } diff --git a/tools/bit/make.h b/tools/bit/make.h index bb83c6e14226..1c9504d62d46 100644 --- a/tools/bit/make.h +++ b/tools/bit/make.h @@ -31,7 +31,7 @@ struct Module vector<string> installed; }; -string get_build_var(const string& buildTop, const string& name, bool quiet); +string get_build_var(const string& name, bool quiet); /** * Poke around in the out directory and try to find a device name that matches diff --git a/tools/fonts/fontchain_lint.py b/tools/fonts/fontchain_lint.py index c6ad4c2aa396..dcb90e411d34 100755 --- a/tools/fonts/fontchain_lint.py +++ b/tools/fonts/fontchain_lint.py @@ -13,6 +13,7 @@ EMOJI_VS = 0xFE0F LANG_TO_SCRIPT = { 'as': 'Beng', + 'be': 'Cyrl', 'bg': 'Cyrl', 'bn': 'Beng', 'cu': 'Cyrl', @@ -33,6 +34,7 @@ LANG_TO_SCRIPT = { 'ja': 'Jpan', 'kn': 'Knda', 'ko': 'Kore', + 'la': 'Latn', 'ml': 'Mlym', 'mn': 'Cyrl', 'mr': 'Deva', |