diff options
| -rw-r--r-- | api/current.txt | 1 | ||||
| -rw-r--r-- | api/system-current.txt | 1 | ||||
| -rw-r--r-- | api/test-current.txt | 1 | ||||
| -rw-r--r-- | core/java/android/provider/BlockedNumberContract.java | 32 | ||||
| -rw-r--r-- | libs/hwui/BakedOpDispatcher.cpp | 24 | ||||
| -rw-r--r-- | media/java/android/media/tv/TvContract.java | 6 | ||||
| -rw-r--r-- | services/core/java/com/android/server/am/ActivityManagerService.java | 33 | ||||
| -rw-r--r-- | services/core/java/com/android/server/am/ActivityStackSupervisor.java | 6 | ||||
| -rw-r--r-- | services/core/java/com/android/server/am/RecentTasks.java | 81 | ||||
| -rw-r--r-- | services/core/java/com/android/server/am/TaskPersister.java | 120 | ||||
| -rw-r--r-- | services/core/java/com/android/server/tv/TvInputHardwareManager.java | 81 | ||||
| -rw-r--r-- | services/core/java/com/android/server/tv/TvInputManagerService.java | 1 |
12 files changed, 326 insertions, 61 deletions
diff --git a/api/current.txt b/api/current.txt index 2ccf50670f3e..45869adf04b2 100644 --- a/api/current.txt +++ b/api/current.txt @@ -22753,6 +22753,7 @@ package android.media.tv { method public static final android.net.Uri buildProgramsUriForChannel(android.net.Uri, long, long); method public static final android.net.Uri buildRecordedProgramUri(long); field public static final java.lang.String AUTHORITY = "android.media.tv"; + field public static final java.lang.String PERMISSION_READ_TV_LISTINGS = "android.permission.READ_TV_LISTINGS"; } public static abstract interface TvContract.BaseTvColumns implements android.provider.BaseColumns { diff --git a/api/system-current.txt b/api/system-current.txt index c40b4f297b1f..8345980bf46d 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -24376,6 +24376,7 @@ package android.media.tv { method public static final android.net.Uri buildRecordedProgramUri(long); method public static final boolean isChannelUriForPassthroughInput(android.net.Uri); field public static final java.lang.String AUTHORITY = "android.media.tv"; + field public static final java.lang.String PERMISSION_READ_TV_LISTINGS = "android.permission.READ_TV_LISTINGS"; } public static abstract interface TvContract.BaseTvColumns implements android.provider.BaseColumns { diff --git a/api/test-current.txt b/api/test-current.txt index 4b6b9be8fa6a..d0650edec264 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -22762,6 +22762,7 @@ package android.media.tv { method public static final android.net.Uri buildProgramsUriForChannel(android.net.Uri, long, long); method public static final android.net.Uri buildRecordedProgramUri(long); field public static final java.lang.String AUTHORITY = "android.media.tv"; + field public static final java.lang.String PERMISSION_READ_TV_LISTINGS = "android.permission.READ_TV_LISTINGS"; } public static abstract interface TvContract.BaseTvColumns implements android.provider.BaseColumns { diff --git a/core/java/android/provider/BlockedNumberContract.java b/core/java/android/provider/BlockedNumberContract.java index ed7c7c5e4868..b9213513cc17 100644 --- a/core/java/android/provider/BlockedNumberContract.java +++ b/core/java/android/provider/BlockedNumberContract.java @@ -239,20 +239,20 @@ public class BlockedNumberContract { public static class SystemContract { /** * A protected broadcast intent action for letting components with - * {@link android.Manifest.permission#READ_BLOCKED_NUMBERS} know that the block suppressal - * status as returned by {@link #getBlockSuppressalStatus(Context)} has been updated. + * {@link android.Manifest.permission#READ_BLOCKED_NUMBERS} know that the block suppression + * status as returned by {@link #getBlockSuppressionStatus(Context)} has been updated. */ - public static final String ACTION_BLOCK_SUPPRESSAL_STATE_CHANGED = - "android.provider.action.BLOCK_SUPPRESSAL_STATE_CHANGED"; + public static final String ACTION_BLOCK_SUPPRESSION_STATE_CHANGED = + "android.provider.action.BLOCK_SUPPRESSION_STATE_CHANGED"; public static final String METHOD_NOTIFY_EMERGENCY_CONTACT = "notify_emergency_contact"; - public static final String METHOD_END_BLOCK_SUPPRESSAL = "end_block_suppressal"; + public static final String METHOD_END_BLOCK_SUPPRESSION = "end_block_suppression"; public static final String METHOD_SHOULD_SYSTEM_BLOCK_NUMBER = "should_system_block_number"; - public static final String METHOD_GET_BLOCK_SUPPRESSAL_STATUS = - "get_block_suppresal_status"; + public static final String METHOD_GET_BLOCK_SUPPRESSION_STATUS = + "get_block_suppression_status"; public static final String RES_IS_BLOCKING_SUPPRESSED = "blocking_suppressed"; @@ -264,7 +264,7 @@ public class BlockedNumberContract { * <p> This results in {@link #shouldSystemBlockNumber} returning {@code false} independent * of the contents of the provider for a duration defined by * {@link android.telephony.CarrierConfigManager#KEY_DURATION_BLOCKING_DISABLED_AFTER_EMERGENCY_INT} - * the provider unless {@link #endBlockSuppressal(Context)} is called. + * the provider unless {@link #endBlockSuppression(Context)} is called. */ public static void notifyEmergencyContact(Context context) { context.getContentResolver().call( @@ -275,9 +275,9 @@ public class BlockedNumberContract { * Notifies the provider to disable suppressing blocking. If emergency services were not * contacted recently at all, calling this method is a no-op. */ - public static void endBlockSuppressal(Context context) { + public static void endBlockSuppression(Context context) { context.getContentResolver().call( - AUTHORITY_URI, METHOD_END_BLOCK_SUPPRESSAL, null, null); + AUTHORITY_URI, METHOD_END_BLOCK_SUPPRESSION, null, null); } /** @@ -292,26 +292,26 @@ public class BlockedNumberContract { return res != null && res.getBoolean(RES_NUMBER_IS_BLOCKED, false); } - public static BlockSuppressalStatus getBlockSuppressalStatus(Context context) { + public static BlockSuppressionStatus getBlockSuppressionStatus(Context context) { final Bundle res = context.getContentResolver().call( - AUTHORITY_URI, METHOD_GET_BLOCK_SUPPRESSAL_STATUS, null, null); - return new BlockSuppressalStatus(res.getBoolean(RES_IS_BLOCKING_SUPPRESSED, false), + AUTHORITY_URI, METHOD_GET_BLOCK_SUPPRESSION_STATUS, null, null); + return new BlockSuppressionStatus(res.getBoolean(RES_IS_BLOCKING_SUPPRESSED, false), res.getLong(RES_BLOCKING_SUPPRESSED_UNTIL_TIMESTAMP, 0)); } /** * Represents the current status of {@link #shouldSystemBlockNumber(Context, String)}. If * emergency services have been contacted recently, {@link #isSuppressed} is {@code true}, - * and blocking is disabled until the timestamp {@link #untilTimestampMillis}. + * and blocking is disabled until the timestamp {@link #untilTimestampMillis}. */ - public static class BlockSuppressalStatus { + public static class BlockSuppressionStatus { public final boolean isSuppressed; /** * Timestamp in milliseconds from epoch. */ public final long untilTimestampMillis; - public BlockSuppressalStatus(boolean isSuppressed, long untilTimestampMillis) { + public BlockSuppressionStatus(boolean isSuppressed, long untilTimestampMillis) { this.isSuppressed = isSuppressed; this.untilTimestampMillis = untilTimestampMillis; } diff --git a/libs/hwui/BakedOpDispatcher.cpp b/libs/hwui/BakedOpDispatcher.cpp index f83e1faf9c8a..78764b5f4448 100644 --- a/libs/hwui/BakedOpDispatcher.cpp +++ b/libs/hwui/BakedOpDispatcher.cpp @@ -334,15 +334,15 @@ static void renderConvexPath(BakedOpRenderer& renderer, const BakedOpState& stat } static void renderPathTexture(BakedOpRenderer& renderer, const BakedOpState& state, - PathTexture& texture, const RecordedOp& op) { + float xOffset, float yOffset, PathTexture& texture, const SkPaint& paint) { Rect dest(texture.width(), texture.height()); - dest.translate(texture.left - texture.offset, - texture.top - texture.offset); + dest.translate(xOffset + texture.left - texture.offset, + yOffset + texture.top - texture.offset); Glop glop; GlopBuilder(renderer.renderState(), renderer.caches(), &glop) .setRoundRectClipState(state.roundRectClipState) .setMeshTexturedUnitQuad(nullptr) - .setFillPathTexturePaint(texture, *(op.paint), state.alpha) + .setFillPathTexturePaint(texture, paint, state.alpha) .setTransform(state.computedState.transform, TransformFlags::None) .setModelViewMapUnitToRect(dest) .build(); @@ -368,7 +368,8 @@ void BakedOpDispatcher::onArcOp(BakedOpRenderer& renderer, const ArcOp& op, cons op.startAngle, op.sweepAngle, op.useCenter, op.paint); const AutoTexture holder(texture); if (CC_LIKELY(holder.texture)) { - renderPathTexture(renderer, state, *texture, op); + renderPathTexture(renderer, state, op.unmappedBounds.left, op.unmappedBounds.right, + *texture, *(op.paint)); } } else { SkRect rect = getBoundsOfFill(op); @@ -519,7 +520,8 @@ void BakedOpDispatcher::onOvalOp(BakedOpRenderer& renderer, const OvalOp& op, co op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), op.paint); const AutoTexture holder(texture); if (CC_LIKELY(holder.texture)) { - renderPathTexture(renderer, state, *texture, op); + renderPathTexture(renderer, state, op.unmappedBounds.left, op.unmappedBounds.right, + *texture, *(op.paint)); } } else { SkPath path; @@ -562,7 +564,9 @@ void BakedOpDispatcher::onPathOp(BakedOpRenderer& renderer, const PathOp& op, co PathTexture* texture = renderer.caches().pathCache.get(op.path, op.paint); const AutoTexture holder(texture); if (CC_LIKELY(holder.texture)) { - renderPathTexture(renderer, state, *texture, op); + // Unlike other callers to renderPathTexture, no offsets are used because PathOp doesn't + // have any translate built in, other than what's in the SkPath itself + renderPathTexture(renderer, state, 0, 0, *texture, *(op.paint)); } } @@ -588,7 +592,8 @@ void BakedOpDispatcher::onRectOp(BakedOpRenderer& renderer, const RectOp& op, co op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), op.paint); const AutoTexture holder(texture); if (CC_LIKELY(holder.texture)) { - renderPathTexture(renderer, state, *texture, op); + renderPathTexture(renderer, state, op.unmappedBounds.left, op.unmappedBounds.top, + *texture, *(op.paint)); } } else { SkPath path; @@ -622,7 +627,8 @@ void BakedOpDispatcher::onRoundRectOp(BakedOpRenderer& renderer, const RoundRect op.rx, op.ry, op.paint); const AutoTexture holder(texture); if (CC_LIKELY(holder.texture)) { - renderPathTexture(renderer, state, *texture, op); + renderPathTexture(renderer, state, op.unmappedBounds.left, op.unmappedBounds.top, + *texture, *(op.paint)); } } else { const VertexBuffer* buffer = renderer.caches().tessellationCache.getRoundRect( diff --git a/media/java/android/media/tv/TvContract.java b/media/java/android/media/tv/TvContract.java index 1c11842cf01c..3e6aa9fb2c90 100644 --- a/media/java/android/media/tv/TvContract.java +++ b/media/java/android/media/tv/TvContract.java @@ -53,6 +53,12 @@ public final class TvContract { /** The authority for the TV provider. */ public static final String AUTHORITY = "android.media.tv"; + /** + * Permission to read TV listings. This is required to read all the TV channel and program + * information available on the system. + */ + public static final String PERMISSION_READ_TV_LISTINGS = "android.permission.READ_TV_LISTINGS"; + private static final String PATH_CHANNEL = "channel"; private static final String PATH_PROGRAM = "program"; private static final String PATH_RECORDED_PROGRAM = "recorded_program"; diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 8c04fbc17e05..5ec2aca3bc0e 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -1894,7 +1894,7 @@ public final class ActivityManagerService extends ActivityManagerNative case SYSTEM_USER_UNLOCK_MSG: { final int userId = msg.arg1; mSystemServiceManager.unlockUser(userId); - mRecentTasks.cleanupLocked(userId); + mRecentTasks.loadUserRecentsLocked(userId); installEncryptionUnawareProviders(userId); break; } @@ -2541,7 +2541,7 @@ public final class ActivityManagerService extends ActivityManagerNative } void onUserStoppedLocked(int userId) { - mRecentTasks.unloadUserRecentsLocked(userId); + mRecentTasks.unloadUserDataFromMemoryLocked(userId); } public void initPowerManagement() { @@ -8739,6 +8739,10 @@ public final class ActivityManagerService extends ActivityManagerNative android.Manifest.permission.GET_DETAILED_TASKS) == PackageManager.PERMISSION_GRANTED; + if (!isUserRunning(userId, ActivityManager.FLAG_AND_UNLOCKED)) { + Slog.i(TAG, "user " + userId + " is still locked. Cannot load recents"); + return Collections.emptyList(); + } mRecentTasks.loadUserRecentsLocked(userId); final int recentsCount = mRecentTasks.size(); @@ -12560,7 +12564,7 @@ public final class ActivityManagerService extends ActivityManagerNative // Make sure we have the current profile info, since it is needed for security checks. mUserController.onSystemReady(); - mRecentTasks.onSystemReady(); + mRecentTasks.onSystemReadyLocked(); // Check to see if there are any update receivers to run. if (!mDidUpdate) { if (mWaitingUpdate) { @@ -18629,8 +18633,15 @@ public final class ActivityManagerService extends ActivityManagerNative } } if ((cr.flags&Context.BIND_NOT_FOREGROUND) == 0) { - if (client.curSchedGroup == Process.THREAD_GROUP_DEFAULT) { - schedGroup = Process.THREAD_GROUP_DEFAULT; + // This will treat important bound services identically to + // the top app, which may behave differently than generic + // foreground work. + if (client.curSchedGroup > schedGroup) { + if ((cr.flags&Context.BIND_IMPORTANT) != 0) { + schedGroup = client.curSchedGroup; + } else { + schedGroup = Process.THREAD_GROUP_DEFAULT; + } } if (clientProcState <= ActivityManager.PROCESS_STATE_TOP) { if (clientProcState == ActivityManager.PROCESS_STATE_TOP) { @@ -18694,11 +18705,15 @@ public final class ActivityManagerService extends ActivityManagerNative final ActivityRecord a = cr.activity; if ((cr.flags&Context.BIND_ADJUST_WITH_ACTIVITY) != 0) { if (a != null && adj > ProcessList.FOREGROUND_APP_ADJ && - (a.visible || a.state == ActivityState.RESUMED - || a.state == ActivityState.PAUSING)) { + (a.visible || a.state == ActivityState.RESUMED || + a.state == ActivityState.PAUSING)) { adj = ProcessList.FOREGROUND_APP_ADJ; if ((cr.flags&Context.BIND_NOT_FOREGROUND) == 0) { - schedGroup = Process.THREAD_GROUP_DEFAULT; + if ((cr.flags&Context.BIND_IMPORTANT) != 0) { + schedGroup = Process.THREAD_GROUP_TOP_APP; + } else { + schedGroup = Process.THREAD_GROUP_DEFAULT; + } } app.cached = false; app.adjType = "service"; @@ -18778,7 +18793,7 @@ public final class ActivityManagerService extends ActivityManagerNative if (procState > clientProcState) { procState = clientProcState; } - if (client.curSchedGroup == Process.THREAD_GROUP_DEFAULT) { + if (client.curSchedGroup > schedGroup) { schedGroup = Process.THREAD_GROUP_DEFAULT; } } diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java index 9562f94f5015..257e333dd319 100644 --- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java @@ -719,14 +719,14 @@ public final class ActivityStackSupervisor implements DisplayListener { } int getNextTaskIdForUserLocked(int userId) { - mRecentTasks.loadUserRecentsLocked(userId); final int currentTaskId = mCurTaskIdForUser.get(userId, userId * MAX_TASK_IDS_PER_USER); // for a userId u, a taskId can only be in the range // [u*MAX_TASK_IDS_PER_USER, (u+1)*MAX_TASK_IDS_PER_USER-1], so if MAX_TASK_IDS_PER_USER // was 10, user 0 could only have taskIds 0 to 9, user 1: 10 to 19, user 2: 20 to 29, so on. int candidateTaskId = currentTaskId; - while (anyTaskForIdLocked(candidateTaskId, !RESTORE_FROM_RECENTS, - INVALID_STACK_ID) != null) { + while (mRecentTasks.taskIdTakenForUserLocked(candidateTaskId, userId) + || anyTaskForIdLocked(candidateTaskId, !RESTORE_FROM_RECENTS, + INVALID_STACK_ID) != null) { candidateTaskId++; if (candidateTaskId == (userId + 1) * MAX_TASK_IDS_PER_USER) { // Wrap around as there will be smaller task ids that are available now. diff --git a/services/core/java/com/android/server/am/RecentTasks.java b/services/core/java/com/android/server/am/RecentTasks.java index 9c139d524bc1..7209814312af 100644 --- a/services/core/java/com/android/server/am/RecentTasks.java +++ b/services/core/java/com/android/server/am/RecentTasks.java @@ -37,8 +37,12 @@ import android.content.pm.PackageManager; import android.graphics.Bitmap; import android.os.Environment; import android.os.RemoteException; +import android.os.SystemClock; import android.os.UserHandle; +import android.provider.Settings.System; +import android.util.ArraySet; import android.util.Slog; +import android.util.SparseArray; import android.util.SparseBooleanArray; import java.io.File; @@ -59,12 +63,22 @@ class RecentTasks extends ArrayList<TaskRecord> { // Maximum number recent bitmaps to keep in memory. private static final int MAX_RECENT_BITMAPS = 3; + private static final int DEFAULT_INITIAL_CAPACITY = 5; /** * Save recent tasks information across reboots. */ private final TaskPersister mTaskPersister; - private final SparseBooleanArray mUsersWithRecentsLoaded = new SparseBooleanArray(5); + private final ActivityManagerService mService; + private final SparseBooleanArray mUsersWithRecentsLoaded = new SparseBooleanArray( + DEFAULT_INITIAL_CAPACITY); + + /** + * Stores for each user task ids that are taken by tasks residing in persistent storage. These + * tasks may or may not currently be in memory. + */ + final SparseArray<SparseBooleanArray> mPersistedTaskIds = new SparseArray<>( + DEFAULT_INITIAL_CAPACITY); // Mainly to avoid object recreation on multiple calls. private final ArrayList<TaskRecord> mTmpRecents = new ArrayList<TaskRecord>(); @@ -75,6 +89,7 @@ class RecentTasks extends ArrayList<TaskRecord> { RecentTasks(ActivityManagerService service, ActivityStackSupervisor mStackSupervisor) { File systemDir = Environment.getDataSystemDirectory(); + mService = service; mTaskPersister = new TaskPersister(systemDir, mStackSupervisor, service, this); mStackSupervisor.setRecentTasks(this); } @@ -87,6 +102,8 @@ class RecentTasks extends ArrayList<TaskRecord> { */ void loadUserRecentsLocked(int userId) { if (!mUsersWithRecentsLoaded.get(userId)) { + // Load the task ids if not loaded. + loadPersistedTaskIdsForUserLocked(userId); Slog.i(TAG, "Loading recents for user " + userId + " into memory."); addAll(mTaskPersister.restoreTasksForUserLocked(userId)); cleanupLocked(userId); @@ -94,21 +111,49 @@ class RecentTasks extends ArrayList<TaskRecord> { } } + private void loadPersistedTaskIdsForUserLocked(int userId) { + // An empty instead of a null set here means that no persistent taskIds were present + // on file when we loaded them. + if (mPersistedTaskIds.get(userId) == null) { + mPersistedTaskIds.put(userId, mTaskPersister.loadPersistedTaskIdsForUser(userId)); + } + } + + boolean taskIdTakenForUserLocked(int taskId, int userId) { + loadPersistedTaskIdsForUserLocked(userId); + return mPersistedTaskIds.get(userId).get(taskId); + } + void notifyTaskPersisterLocked(TaskRecord task, boolean flush) { if (task != null && task.stack != null && task.stack.isHomeStack()) { // Never persist the home stack. return; } + syncPersistentTaskIdsLocked(); mTaskPersister.wakeup(task, flush); } - void onSystemReady() { - clear(); - loadUserRecentsLocked(UserHandle.USER_SYSTEM); - startPersisting(); + private void syncPersistentTaskIdsLocked() { + for (int i = mPersistedTaskIds.size() - 1; i >= 0; i--) { + int userId = mPersistedTaskIds.keyAt(i); + if (mUsersWithRecentsLoaded.get(userId)) { + // Recents are loaded only after task ids are loaded. Therefore, the set of taskids + // referenced here should not be null. + mPersistedTaskIds.valueAt(i).clear(); + } + } + for (int i = size() - 1; i >= 0; i--) { + TaskRecord task = get(i); + if (task.isPersistable && (task.stack == null || !task.stack.isHomeStack())) { + // Set of persisted taskIds for task.userId should not be null here + mPersistedTaskIds.get(task.userId).put(task.taskId, true); + } + } } - void startPersisting() { + + void onSystemReadyLocked() { + clear(); mTaskPersister.startPersisting(); } @@ -125,11 +170,14 @@ class RecentTasks extends ArrayList<TaskRecord> { } void flush() { + synchronized (mService) { + syncPersistentTaskIdsLocked(); + } mTaskPersister.flush(); } /** - * Returns all userIds for which recents from storage are loaded + * Returns all userIds for which recents from persistent storage are loaded into this list. * * @return an array of userIds. */ @@ -149,12 +197,7 @@ class RecentTasks extends ArrayList<TaskRecord> { return usersWithRecentsLoaded; } - /** - * Removes recent tasks for this user if they are loaded, does not do anything otherwise. - * - * @param userId the user id. - */ - void unloadUserRecentsLocked(int userId) { + private void unloadUserRecentsLocked(int userId) { if (mUsersWithRecentsLoaded.get(userId)) { Slog.i(TAG, "Unloading recents for user " + userId + " from memory."); mUsersWithRecentsLoaded.delete(userId); @@ -162,6 +205,18 @@ class RecentTasks extends ArrayList<TaskRecord> { } } + /** + * Removes recent tasks and any other state kept in memory for the passed in user. Does not + * touch the information present on persistent storage. + * + * @param userId the id of the user + */ + void unloadUserDataFromMemoryLocked(int userId) { + unloadUserRecentsLocked(userId); + mPersistedTaskIds.delete(userId); + mTaskPersister.unloadUserDataFromMemory(userId); + } + TaskRecord taskForIdLocked(int id) { final int recentsCount = size(); for (int i = 0; i < recentsCount; i++) { diff --git a/services/core/java/com/android/server/am/TaskPersister.java b/services/core/java/com/android/server/am/TaskPersister.java index 283939e29229..11fd3bc61ca6 100644 --- a/services/core/java/com/android/server/am/TaskPersister.java +++ b/services/core/java/com/android/server/am/TaskPersister.java @@ -16,6 +16,9 @@ package com.android.server.am; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.app.ActivityManager; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.os.Debug; @@ -26,11 +29,13 @@ import android.os.SystemClock; import android.util.ArraySet; import android.util.AtomicFile; import android.util.Slog; +import android.util.SparseArray; +import android.util.SparseBooleanArray; import android.util.Xml; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.XmlUtils; - import libcore.io.IoUtils; import org.xmlpull.v1.XmlPullParser; @@ -38,9 +43,12 @@ import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlSerializer; import java.io.BufferedReader; +import java.io.BufferedWriter; import java.io.File; +import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.FileReader; +import java.io.FileWriter; import java.io.IOException; import java.io.StringWriter; import java.util.ArrayList; @@ -71,6 +79,7 @@ public class TaskPersister { private static final String TASKS_DIRNAME = "recent_tasks"; private static final String TASK_EXTENSION = ".xml"; private static final String IMAGES_DIRNAME = "recent_images"; + private static final String PERSISTED_TASK_IDS_FILENAME = "persisted_taskIds.txt"; static final String IMAGE_EXTENSION = ".png"; private static final String TAG_TASK = "task"; @@ -78,6 +87,7 @@ public class TaskPersister { private final ActivityManagerService mService; private final ActivityStackSupervisor mStackSupervisor; private final RecentTasks mRecentTasks; + private final SparseArray<SparseBooleanArray> mTaskIdsInFile = new SparseArray<>(); /** * Value determines write delay mode as follows: < 0 We are Flushing. No delays between writes @@ -170,6 +180,64 @@ public class TaskPersister { } } + @NonNull + SparseBooleanArray loadPersistedTaskIdsForUser(int userId) { + if (mTaskIdsInFile.get(userId) != null) { + return mTaskIdsInFile.get(userId).clone(); + } + final SparseBooleanArray persistedTaskIds = new SparseBooleanArray(); + BufferedReader reader = null; + String line; + try { + reader = new BufferedReader(new FileReader(getUserPersistedTaskIdsFile(userId))); + while ((line = reader.readLine()) != null) { + for (String taskIdString : line.split("\\s+")) { + int id = Integer.parseInt(taskIdString); + persistedTaskIds.put(id, true); + } + } + } catch (FileNotFoundException e) { + // File doesn't exist. Ignore. + } catch (Exception e) { + Slog.e(TAG, "Error while reading taskIds file for user " + userId, e); + } finally { + IoUtils.closeQuietly(reader); + } + mTaskIdsInFile.put(userId, persistedTaskIds); + return persistedTaskIds.clone(); + } + + private void maybeWritePersistedTaskIdsForUser(@NonNull SparseBooleanArray taskIds, + int userId) { + if (userId < 0) { + return; + } + SparseBooleanArray persistedIdsInFile = mTaskIdsInFile.get(userId); + if (persistedIdsInFile != null && persistedIdsInFile.equals(taskIds)) { + return; + } + final File persistedTaskIdsFile = getUserPersistedTaskIdsFile(userId); + BufferedWriter writer = null; + try { + writer = new BufferedWriter(new FileWriter(persistedTaskIdsFile)); + for (int i = 0; i < taskIds.size(); i++) { + if (taskIds.valueAt(i)) { + writer.write(String.valueOf(taskIds.keyAt(i))); + writer.newLine(); + } + } + } catch (Exception e) { + Slog.e(TAG, "Error while writing taskIds file for user " + userId, e); + } finally { + IoUtils.closeQuietly(writer); + } + mTaskIdsInFile.put(userId, taskIds.clone()); + } + + void unloadUserDataFromMemory(int userId) { + mTaskIdsInFile.delete(userId); + } + void wakeup(TaskRecord task, boolean flush) { synchronized (this) { if (task != null) { @@ -336,14 +404,16 @@ public class TaskPersister { File[] recentFiles = userTasksDir.listFiles(); if (recentFiles == null) { - Slog.e(TAG, "restoreTasksForUser: Unable to list files from " + userTasksDir); + Slog.e(TAG, "restoreTasksForUserLocked: Unable to list files from " + userTasksDir); return tasks; } for (int taskNdx = 0; taskNdx < recentFiles.length; ++taskNdx) { File taskFile = recentFiles[taskNdx]; - if (DEBUG) Slog.d(TAG, "restoreTasksForUser: userId=" + userId - + ", taskFile=" + taskFile.getName()); + if (DEBUG) { + Slog.d(TAG, "restoreTasksForUserLocked: userId=" + userId + + ", taskFile=" + taskFile.getName()); + } BufferedReader reader = null; boolean deleteFile = false; try { @@ -366,20 +436,29 @@ public class TaskPersister { // out the stuff we just read, if we don't write it we will // read the same thing again. // mWriteQueue.add(new TaskWriteQueueItem(task)); + final int taskId = task.taskId; - mStackSupervisor.setNextTaskIdForUserLocked(taskId, userId); - // Check if it's a valid user id. Don't add tasks for removed users. - if (userId == task.userId) { + if (mStackSupervisor.anyTaskForIdLocked(taskId, + /* restoreFromRecents= */ false, 0) != null) { + // Should not happen. + Slog.wtf(TAG, "Existing task with taskId " + taskId + "found"); + } else if (userId != task.userId) { + // Should not happen. + Slog.wtf(TAG, "Task with userId " + task.userId + " found in " + + userTasksDir.getAbsolutePath()); + } else { + // Looks fine. + mStackSupervisor.setNextTaskIdForUserLocked(taskId, userId); task.isPersistable = true; tasks.add(task); recoveredTaskIds.add(taskId); } } else { - Slog.e(TAG, "restoreTasksForUser: Unable to restore taskFile=" + Slog.e(TAG, "restoreTasksForUserLocked: Unable to restore taskFile=" + taskFile + ": " + fileToString(taskFile)); } } else { - Slog.wtf(TAG, "restoreTasksForUser: Unknown xml event=" + event + Slog.wtf(TAG, "restoreTasksForUserLocked: Unknown xml event=" + event + " name=" + name); } } @@ -454,6 +533,20 @@ public class TaskPersister { } } + private void writeTaskIdsFiles() { + int candidateUserIds[]; + synchronized (mService) { + candidateUserIds = mRecentTasks.usersWithRecentsLoadedLocked(); + } + SparseBooleanArray taskIdsToSave; + for (int userId : candidateUserIds) { + synchronized (mService) { + taskIdsToSave = mRecentTasks.mPersistedTaskIds.get(userId).clone(); + } + maybeWritePersistedTaskIdsForUser(taskIdsToSave, userId); + } + } + private void removeObsoleteFiles(ArraySet<Integer> persistentTaskIds) { int[] candidateUserIds; synchronized (mService) { @@ -472,8 +565,12 @@ public class TaskPersister { return BitmapFactory.decodeFile(filename); } + static File getUserPersistedTaskIdsFile(int userId) { + return new File(Environment.getDataSystemDeDirectory(userId), PERSISTED_TASK_IDS_FILENAME); + } + static File getUserTasksDir(int userId) { - File userTasksDir = new File(Environment.getUserSystemDirectory(userId), TASKS_DIRNAME); + File userTasksDir = new File(Environment.getDataSystemCeDirectory(userId), TASKS_DIRNAME); if (!userTasksDir.exists()) { if (!userTasksDir.mkdir()) { @@ -485,7 +582,7 @@ public class TaskPersister { } static File getUserImagesDir(int userId) { - File userImagesDir = new File(Environment.getUserSystemDirectory(userId), IMAGES_DIRNAME); + File userImagesDir = new File(Environment.getDataSystemCeDirectory(userId), IMAGES_DIRNAME); if (!userImagesDir.exists()) { if (!userImagesDir.mkdir()) { @@ -535,6 +632,7 @@ public class TaskPersister { } removeObsoleteFiles(persistentTaskIds); } + writeTaskIdsFiles(); // If mNextWriteTime, then don't delay between each call to saveToXml(). final WriteQueueItem item; diff --git a/services/core/java/com/android/server/tv/TvInputHardwareManager.java b/services/core/java/com/android/server/tv/TvInputHardwareManager.java index 4e96d71d7e18..bf281d6fb471 100644 --- a/services/core/java/com/android/server/tv/TvInputHardwareManager.java +++ b/services/core/java/com/android/server/tv/TvInputHardwareManager.java @@ -24,6 +24,7 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.pm.PackageManager; import android.hardware.hdmi.HdmiControlManager; import android.hardware.hdmi.HdmiDeviceInfo; import android.hardware.hdmi.HdmiHotplugEvent; @@ -45,6 +46,7 @@ import android.media.tv.ITvInputHardwareCallback; import android.media.tv.TvInputHardwareInfo; import android.media.tv.TvInputInfo; import android.media.tv.TvStreamConfig; +import android.os.Binder; import android.os.Handler; import android.os.IBinder; import android.os.Message; @@ -57,9 +59,13 @@ import android.util.SparseBooleanArray; import android.view.KeyEvent; import android.view.Surface; +import com.android.internal.util.IndentingPrintWriter; import com.android.server.SystemService; +import java.io.FileDescriptor; +import java.io.PrintWriter; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.Iterator; import java.util.LinkedList; @@ -549,6 +555,70 @@ class TvInputHardwareManager implements TvInputHal.Callback { return (float) mCurrentIndex / (float) mCurrentMaxIndex; } + public void dump(FileDescriptor fd, final PrintWriter writer, String[] args) { + final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " "); + if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) + != PackageManager.PERMISSION_GRANTED) { + pw.println("Permission Denial: can't dump TvInputHardwareManager from pid=" + + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()); + return; + } + + synchronized (mLock) { + pw.println("TvInputHardwareManager Info:"); + pw.increaseIndent(); + pw.println("mConnections: deviceId -> Connection"); + pw.increaseIndent(); + for (int i = 0; i < mConnections.size(); i++) { + int deviceId = mConnections.keyAt(i); + Connection mConnection = mConnections.valueAt(i); + pw.println(deviceId + ": " + mConnection); + + } + pw.decreaseIndent(); + + pw.println("mHardwareList:"); + pw.increaseIndent(); + for (TvInputHardwareInfo tvInputHardwareInfo : mHardwareList) { + pw.println(tvInputHardwareInfo); + } + pw.decreaseIndent(); + + pw.println("mHdmiDeviceList:"); + pw.increaseIndent(); + for (HdmiDeviceInfo hdmiDeviceInfo : mHdmiDeviceList) { + pw.println(hdmiDeviceInfo); + } + pw.decreaseIndent(); + + pw.println("mHardwareInputIdMap: deviceId -> inputId"); + pw.increaseIndent(); + for (int i = 0 ; i < mHardwareInputIdMap.size(); i++) { + int deviceId = mHardwareInputIdMap.keyAt(i); + String inputId = mHardwareInputIdMap.valueAt(i); + pw.println(deviceId + ": " + inputId); + } + pw.decreaseIndent(); + + pw.println("mHdmiInputIdMap: id -> inputId"); + pw.increaseIndent(); + for (int i = 0; i < mHdmiInputIdMap.size(); i++) { + int id = mHdmiInputIdMap.keyAt(i); + String inputId = mHdmiInputIdMap.valueAt(i); + pw.println(id + ": " + inputId); + } + pw.decreaseIndent(); + + pw.println("mInputMap: inputId -> inputInfo"); + pw.increaseIndent(); + for(Map.Entry<String, TvInputInfo> entry : mInputMap.entrySet()) { + pw.println(entry.getKey() + ": " + entry.getValue()); + } + pw.decreaseIndent(); + pw.decreaseIndent(); + } + } + private class Connection implements IBinder.DeathRecipient { private final TvInputHardwareInfo mHardwareInfo; private TvInputInfo mInfo; @@ -641,6 +711,17 @@ class TvInputHardwareManager implements TvInputHal.Callback { resetLocked(null, null, null, null, null); } } + + public String toString() { + return "Connection{" + + " mHardwareInfo: " + mHardwareInfo + + ", mInfo: " + mInfo + + ", mCallback: " + mCallback + + ", mConfigs: " + Arrays.toString(mConfigs) + + ", mCallingUid: " + mCallingUid + + ", mResolvedUserId: " + mResolvedUserId + + " }"; + } } private class TvInputHardwareImpl extends ITvInputHardware.Stub { diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java index 628c6270f1d6..8a2729eec805 100644 --- a/services/core/java/com/android/server/tv/TvInputManagerService.java +++ b/services/core/java/com/android/server/tv/TvInputManagerService.java @@ -1923,6 +1923,7 @@ public final class TvInputManagerService extends SystemService { pw.decreaseIndent(); } } + mTvInputHardwareManager.dump(fd, writer, args); } } |