diff options
Diffstat (limited to 'services')
31 files changed, 1419 insertions, 211 deletions
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index 7ecf248..5527528 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -343,12 +343,6 @@ public class ConnectivityService extends IConnectivityManager.Stub { private static final int EVENT_INET_CONDITION_HOLD_END = 5; /** - * used internally to set enable/disable cellular data - * arg1 = ENBALED or DISABLED - */ - private static final int EVENT_SET_MOBILE_DATA = 7; - - /** * used internally to clear a wakelock when transitioning * from one net to another */ @@ -1822,20 +1816,6 @@ public class ConnectivityService extends IConnectivityManager.Stub { return true; } - /** - * @see ConnectivityManager#getMobileDataEnabled() - */ - public boolean getMobileDataEnabled() { - // TODO: This detail should probably be in DataConnectionTracker's - // which is where we store the value and maybe make this - // asynchronous. - enforceAccessPermission(); - boolean retVal = Settings.Global.getInt(mContext.getContentResolver(), - Settings.Global.MOBILE_DATA, 1) == 1; - if (VDBG) log("getMobileDataEnabled returning " + retVal); - return retVal; - } - public void setDataDependency(int networkType, boolean met) { enforceConnectivityInternalPermission(); @@ -1908,22 +1888,6 @@ public class ConnectivityService extends IConnectivityManager.Stub { } }; - /** - * @see ConnectivityManager#setMobileDataEnabled(boolean) - */ - public void setMobileDataEnabled(boolean enabled) { - enforceChangePermission(); - if (DBG) log("setMobileDataEnabled(" + enabled + ")"); - - mHandler.sendMessage(mHandler.obtainMessage(EVENT_SET_MOBILE_DATA, - (enabled ? ENABLED : DISABLED), 0)); - } - - private void handleSetMobileData(boolean enabled) { - // TODO - handle this - probably generalize passing in a transport type and send to the - // factories? - } - @Override public void setPolicyDataEnable(int networkType, boolean enabled) { // only someone like NPMS should only be calling us @@ -3315,11 +3279,6 @@ public class ConnectivityService extends IConnectivityManager.Stub { handleInetConditionHoldEnd(netType, sequence); break; } - case EVENT_SET_MOBILE_DATA: { - boolean enabled = (msg.arg1 == ENABLED); - handleSetMobileData(enabled); - break; - } case EVENT_APPLY_GLOBAL_HTTP_PROXY: { handleDeprecatedGlobalHttpProxy(); break; @@ -5647,16 +5606,23 @@ public class ConnectivityService extends IConnectivityManager.Stub { boolean isNewDefault = false; if (DBG) log("handleConnectionValidated for "+newNetwork.name()); // check if any NetworkRequest wants this NetworkAgent - // first check if it satisfies the NetworkCapabilities ArrayList<NetworkAgentInfo> affectedNetworks = new ArrayList<NetworkAgentInfo>(); if (VDBG) log(" new Network has: " + newNetwork.networkCapabilities); for (NetworkRequestInfo nri : mNetworkRequests.values()) { + NetworkAgentInfo currentNetwork = mNetworkForRequestId.get(nri.request.requestId); + if (newNetwork == currentNetwork) { + if (VDBG) log("Network " + newNetwork.name() + " was already satisfying" + + " request " + nri.request.requestId + ". No change."); + keep = true; + continue; + } + + // check if it satisfies the NetworkCapabilities if (VDBG) log(" checking if request is satisfied: " + nri.request); if (nri.request.networkCapabilities.satisfiedByNetworkCapabilities( newNetwork.networkCapabilities)) { // next check if it's better than any current network we're using for // this request - NetworkAgentInfo currentNetwork = mNetworkForRequestId.get(nri.request.requestId); if (VDBG) { log("currentScore = " + (currentNetwork != null ? currentNetwork.currentScore : 0) + @@ -5785,12 +5751,19 @@ public class ConnectivityService extends IConnectivityManager.Stub { } if (state == NetworkInfo.State.CONNECTED) { - // TODO - check if we want it (optimization) try { + // This is likely caused by the fact that this network already + // exists. An example is when a network goes from CONNECTED to + // CONNECTING and back (like wifi on DHCP renew). + // TODO: keep track of which networks we've created, or ask netd + // to tell us whether we've already created this network or not. mNetd.createNetwork(networkAgent.network.netId); } catch (Exception e) { - loge("Error creating Network " + networkAgent.network.netId); + loge("Error creating network " + networkAgent.network.netId + ": " + + e.getMessage()); + return; } + updateLinkProperties(networkAgent, null); notifyNetworkCallbacks(networkAgent, ConnectivityManager.CALLBACK_PRECHECK); networkAgent.networkMonitor.sendMessage(NetworkMonitor.CMD_NETWORK_CONNECTED); diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 88bebcb..fc808ec 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -7085,6 +7085,10 @@ public final class ActivityManagerService extends ActivityManagerNative * Creates a new RecentTaskInfo from a TaskRecord. */ private ActivityManager.RecentTaskInfo createRecentTaskInfoFromTaskRecord(TaskRecord tr) { + // Update the task description to reflect any changes in the task stack + tr.updateTaskDescription(); + + // Compose the recent task info ActivityManager.RecentTaskInfo rti = new ActivityManager.RecentTaskInfo(); rti.id = tr.mActivities.isEmpty() ? -1 : tr.taskId; @@ -9574,11 +9578,13 @@ public final class ActivityManagerService extends ActivityManagerNative return; } - mRecentTasks = mTaskPersister.restoreTasksLocked(); - if (!mRecentTasks.isEmpty()) { - mStackSupervisor.createStackForRestoredTaskHistory(mRecentTasks); + if (mRecentTasks == null) { + mRecentTasks = mTaskPersister.restoreTasksLocked(); + if (!mRecentTasks.isEmpty()) { + mStackSupervisor.createStackForRestoredTaskHistory(mRecentTasks); + } + mTaskPersister.startPersisting(); } - mTaskPersister.startPersisting(); // Check to see if there are any update receivers to run. if (!mDidUpdate) { diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java index b948c41..b429b93 100755 --- a/services/core/java/com/android/server/am/ActivityRecord.java +++ b/services/core/java/com/android/server/am/ActivityRecord.java @@ -1154,13 +1154,15 @@ final class ActivityRecord { } if (intent == null) { - Slog.e(TAG, "restoreActivity error intent=" + intent); - return null; + throw new XmlPullParserException("restoreActivity error intent=" + intent); } final ActivityManagerService service = stackSupervisor.mService; final ActivityInfo aInfo = stackSupervisor.resolveActivity(intent, resolvedType, 0, null, null, userId); + if (aInfo == null) { + throw new XmlPullParserException("restoreActivity resolver error."); + } final ActivityRecord r = new ActivityRecord(service, /*caller*/null, launchedFromUid, launchedFromPackage, intent, resolvedType, aInfo, service.getConfiguration(), null, null, 0, componentSpecified, stackSupervisor, null, null); diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java index 534fd90..1804d03 100755 --- a/services/core/java/com/android/server/am/ActivityStack.java +++ b/services/core/java/com/android/server/am/ActivityStack.java @@ -33,6 +33,7 @@ import static com.android.server.am.ActivityManagerService.VALIDATE_TOKENS; import static com.android.server.am.ActivityStackSupervisor.DEBUG_ADD_REMOVE; import static com.android.server.am.ActivityStackSupervisor.DEBUG_APP; import static com.android.server.am.ActivityStackSupervisor.DEBUG_SAVED_STATE; +import static com.android.server.am.ActivityStackSupervisor.DEBUG_SCREENSHOTS; import static com.android.server.am.ActivityStackSupervisor.DEBUG_STATES; import static com.android.server.am.ActivityStackSupervisor.HOME_STACK_ID; @@ -342,6 +343,10 @@ final class ActivityStack { mWindowManager = mService.mWindowManager; mStackId = activityContainer.mStackId; mCurrentUser = mService.mCurrentUserId; + // Get the activity screenshot thumbnail dimensions + Resources res = mService.mContext.getResources(); + mThumbnailWidth = res.getDimensionPixelSize(com.android.internal.R.dimen.thumbnail_width); + mThumbnailHeight = res.getDimensionPixelSize(com.android.internal.R.dimen.thumbnail_height); } /** @@ -725,42 +730,54 @@ final class ActivityStack { } } + /** + * This resets the saved state from the last screenshot, forcing a new screenshot to be taken + * again when requested. + */ + private void invalidateLastScreenshot() { + mLastScreenshotActivity = null; + if (mLastScreenshotBitmap != null) { + mLastScreenshotBitmap.recycle(); + } + mLastScreenshotBitmap = null; + } + public final Bitmap screenshotActivities(ActivityRecord who) { + if (DEBUG_SCREENSHOTS) Slog.d(TAG, "screenshotActivities: " + who); if (who.noDisplay) { + if (DEBUG_SCREENSHOTS) Slog.d(TAG, "\tNo display"); return null; } TaskRecord tr = who.task; - if (mService.getMostRecentTask() != tr && tr.intent != null && - (tr.intent.getFlags() & Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) != 0) { - // If this task is being excluded from recents, we don't want to take - // the expense of capturing a thumbnail, since we will never show it. + if (mService.getMostRecentTask() != tr || isHomeStack()) { + // This is an optimization -- since we never show Home or Recents within Recents itself, + // we can just go ahead and skip taking the screenshot if this is the home stack. In + // the case where the most recent task is not the task that was supplied, then the stack + // has changed, so invalidate the last screenshot(). + invalidateLastScreenshot(); + if (DEBUG_SCREENSHOTS) Slog.d(TAG, "\tIs Home stack? " + isHomeStack()); return null; } - Resources res = mService.mContext.getResources(); int w = mThumbnailWidth; int h = mThumbnailHeight; - if (w < 0) { - mThumbnailWidth = w = - res.getDimensionPixelSize(com.android.internal.R.dimen.recents_thumbnail_width); - mThumbnailHeight = h = - res.getDimensionPixelSize(com.android.internal.R.dimen.recents_thumbnail_height); - } - if (w > 0) { if (who != mLastScreenshotActivity || mLastScreenshotBitmap == null || mLastScreenshotActivity.state == ActivityState.RESUMED || mLastScreenshotBitmap.getWidth() != w || mLastScreenshotBitmap.getHeight() != h) { + if (DEBUG_SCREENSHOTS) Slog.d(TAG, "\tUpdating screenshot"); mLastScreenshotActivity = who; mLastScreenshotBitmap = mWindowManager.screenshotApplications( who.appToken, Display.DEFAULT_DISPLAY, w, h, SCREENSHOT_FORCE_565); } if (mLastScreenshotBitmap != null) { + if (DEBUG_SCREENSHOTS) Slog.d(TAG, "\tReusing last screenshot"); return mLastScreenshotBitmap.copy(mLastScreenshotBitmap.getConfig(), true); } } + Slog.e(TAG, "Invalid thumbnail dimensions: " + w + "x" + h); return null; } @@ -1038,6 +1055,12 @@ final class ActivityStack { } else { next.cpuTimeAtResume = 0; // Couldn't get the cpu time of process } + + // If we are resuming the activity that we had last screenshotted, then we know it will be + // updated, so invalidate the last screenshot to ensure we take a fresh one when requested + if (next == mLastScreenshotActivity) { + invalidateLastScreenshot(); + } } /** @@ -1859,7 +1882,7 @@ final class ActivityStack { mWindowManager.addAppToken(task.mActivities.indexOf(r), r.appToken, r.task.taskId, mStackId, r.info.screenOrientation, r.fullscreen, (r.info.flags & ActivityInfo.FLAG_SHOW_ON_LOCK_SCREEN) != 0, - r.userId, r.info.configChanges); + r.userId, r.info.configChanges, task.voiceSession != null); if (VALIDATE_TOKENS) { validateAppTokensLocked(); } @@ -1920,7 +1943,7 @@ final class ActivityStack { mWindowManager.addAppToken(task.mActivities.indexOf(r), r.appToken, r.task.taskId, mStackId, r.info.screenOrientation, r.fullscreen, (r.info.flags & ActivityInfo.FLAG_SHOW_ON_LOCK_SCREEN) != 0, r.userId, - r.info.configChanges); + r.info.configChanges, task.voiceSession != null); boolean doShow = true; if (newTask) { // Even though this activity is starting fresh, we still need @@ -1965,7 +1988,7 @@ final class ActivityStack { mWindowManager.addAppToken(task.mActivities.indexOf(r), r.appToken, r.task.taskId, mStackId, r.info.screenOrientation, r.fullscreen, (r.info.flags & ActivityInfo.FLAG_SHOW_ON_LOCK_SCREEN) != 0, r.userId, - r.info.configChanges); + r.info.configChanges, task.voiceSession != null); ActivityOptions.abort(options); options = null; } diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java index e9565d6..0b1c2b8 100644 --- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java @@ -106,6 +106,7 @@ public final class ActivityStackSupervisor implements DisplayListener { static final boolean DEBUG_SAVED_STATE = DEBUG || false; static final boolean DEBUG_STATES = DEBUG || false; static final boolean DEBUG_IDLE = DEBUG || false; + static final boolean DEBUG_SCREENSHOTS = DEBUG || false; public static final int HOME_STACK_ID = 0; @@ -1200,8 +1201,7 @@ public final class ActivityStackSupervisor implements DisplayListener { requestCode = sourceRecord.requestCode; sourceRecord.resultTo = null; if (resultRecord != null) { - resultRecord.removeResultsLocked( - sourceRecord, resultWho, requestCode); + resultRecord.removeResultsLocked(sourceRecord, resultWho, requestCode); } if (sourceRecord.launchedFromUid == callingUid) { // The new activity is being launched from the same uid as the previous @@ -1373,7 +1373,7 @@ public final class ActivityStackSupervisor implements DisplayListener { return err; } - ActivityStack adjustStackFocus(ActivityRecord r) { + ActivityStack adjustStackFocus(ActivityRecord r, boolean newTask) { final TaskRecord task = r.task; if (r.isApplicationActivity() || (task != null && task.isApplicationTask())) { if (task != null) { @@ -1398,7 +1398,8 @@ public final class ActivityStackSupervisor implements DisplayListener { return container.mStack; } - if (mFocusedStack != mHomeStack) { + if (mFocusedStack != mHomeStack && (!newTask || + mFocusedStack.mActivityContainer.isEligibleForNewTasks())) { if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG, "adjustStackFocus: Have a focused stack=" + mFocusedStack); return mFocusedStack; @@ -1451,7 +1452,7 @@ public final class ActivityStackSupervisor implements DisplayListener { // We'll invoke onUserLeaving before onPause only if the launching // activity did not explicitly state that this is an automated launch. - mUserLeaving = (launchFlags&Intent.FLAG_ACTIVITY_NO_USER_ACTION) == 0; + mUserLeaving = (launchFlags & Intent.FLAG_ACTIVITY_NO_USER_ACTION) == 0; if (DEBUG_USER_LEAVING) Slog.v(TAG, "startActivity() => mUserLeaving=" + mUserLeaving); // If the caller has asked not to resume at this point, we make note @@ -1461,7 +1462,8 @@ public final class ActivityStackSupervisor implements DisplayListener { r.delayedResume = true; } - ActivityRecord notTop = (launchFlags&Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP) != 0 ? r : null; + ActivityRecord notTop = + (launchFlags & Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP) != 0 ? r : null; // If the onlyIfNeeded flag is set, then we can do this if the activity // being launched is the same as the one making the call... or, as @@ -1484,9 +1486,11 @@ public final class ActivityStackSupervisor implements DisplayListener { case ActivityInfo.DOCUMENT_LAUNCH_ALWAYS: intent.addFlags( Intent.FLAG_ACTIVITY_NEW_DOCUMENT | Intent.FLAG_ACTIVITY_MULTIPLE_TASK); + launchFlags = intent.getFlags(); break; case ActivityInfo.DOCUMENT_LAUNCH_INTO_EXISTING: intent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT); + launchFlags = intent.getFlags(); break; } final boolean newDocument = intent.isDocument(); @@ -1792,7 +1796,8 @@ public final class ActivityStackSupervisor implements DisplayListener { Slog.e(TAG, "Attempted Lock Task Mode violation r=" + r); return ActivityManager.START_RETURN_LOCK_TASK_MODE_VIOLATION; } - targetStack = adjustStackFocus(r); + newTask = true; + targetStack = adjustStackFocus(r, newTask); targetStack.moveToFront(); if (reuseTask == null) { r.setTask(targetStack.createTaskRecord(getNextTaskId(), @@ -1804,7 +1809,6 @@ public final class ActivityStackSupervisor implements DisplayListener { } else { r.setTask(reuseTask, reuseTask, true); } - newTask = true; if (!movedHome) { if ((launchFlags & (Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_TASK_ON_HOME)) @@ -1872,7 +1876,7 @@ public final class ActivityStackSupervisor implements DisplayListener { // This not being started from an existing activity, and not part // of a new task... just put it in the top task, though these days // this case should never happen. - targetStack = adjustStackFocus(r); + targetStack = adjustStackFocus(r, newTask); targetStack.moveToFront(); ActivityRecord prev = targetStack.topActivity(); r.setTask(prev != null ? prev.task @@ -2269,7 +2273,7 @@ public final class ActivityStackSupervisor implements DisplayListener { mWindowManager.addAppToken(0, r.appToken, taskId, stackId, r.info.screenOrientation, r.fullscreen, (r.info.flags & ActivityInfo.FLAG_SHOW_ON_LOCK_SCREEN) != 0, - r.userId, r.info.configChanges); + r.userId, r.info.configChanges, task.voiceSession != null); } mWindowManager.addTask(taskId, stackId, false); } @@ -2299,7 +2303,12 @@ public final class ActivityStackSupervisor implements DisplayListener { for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) { final ActivityStack stack = stacks.get(stackNdx); if (!r.isApplicationActivity() && !stack.isHomeStack()) { - if (DEBUG_TASKS) Slog.d(TAG, "Skipping stack: " + stack); + if (DEBUG_TASKS) Slog.d(TAG, "Skipping stack: (home activity) " + stack); + continue; + } + if (!stack.mActivityContainer.isEligibleForNewTasks()) { + if (DEBUG_TASKS) Slog.d(TAG, "Skipping stack: (new task not allowed) " + + stack); continue; } final ActivityRecord ar = stack.findTaskLocked(r); @@ -3230,6 +3239,11 @@ public final class ActivityStackSupervisor implements DisplayListener { void setDrawn() { } + // You can always start a new task on a regular ActivityStack. + boolean isEligibleForNewTasks() { + return true; + } + @Override public String toString() { return mIdString + (mActivityDisplay == null ? "N" : "A"); @@ -3310,6 +3324,12 @@ public final class ActivityStackSupervisor implements DisplayListener { } } + // Never start a new task on an ActivityView if it isn't explicitly specified. + @Override + boolean isEligibleForNewTasks() { + return false; + } + private void setSurfaceIfReady() { if (DEBUG_STACK) Slog.v(TAG, "setSurfaceIfReady: mDrawn=" + mDrawn + " mContainerState=" + mContainerState + " mSurface=" + mSurface); diff --git a/services/core/java/com/android/server/am/TaskPersister.java b/services/core/java/com/android/server/am/TaskPersister.java index ba3f2fe..3bfaca9 100644 --- a/services/core/java/com/android/server/am/TaskPersister.java +++ b/services/core/java/com/android/server/am/TaskPersister.java @@ -155,6 +155,7 @@ public class TaskPersister { File taskFile = recentFiles[taskNdx]; if (DEBUG) Slog.d(TAG, "restoreTasksLocked: taskFile=" + taskFile.getName()); BufferedReader reader = null; + boolean deleteFile = false; try { reader = new BufferedReader(new FileReader(taskFile)); final XmlPullParser in = Xml.newPullParser(); @@ -183,10 +184,9 @@ public class TaskPersister { } XmlUtils.skipCurrentTag(in); } - } catch (IOException e) { - Slog.e(TAG, "Unable to parse " + taskFile + ". Error " + e); - } catch (XmlPullParserException e) { - Slog.e(TAG, "Unable to parse " + taskFile + ". Error " + e); + } catch (Exception e) { + Slog.wtf(TAG, "Unable to parse " + taskFile + ". Error " + e); + deleteFile = true; } finally { if (reader != null) { try { @@ -194,6 +194,9 @@ public class TaskPersister { } catch (IOException e) { } } + if (!DEBUG && deleteFile) { + taskFile.delete(); + } } } @@ -220,7 +223,7 @@ public class TaskPersister { return new ArrayList<TaskRecord>(Arrays.asList(tasksArray)); } - private void removeObsoleteFiles(ArraySet<Integer> persistentTaskIds, File[] files) { + private static void removeObsoleteFiles(ArraySet<Integer> persistentTaskIds, File[] files) { for (int fileNdx = 0; fileNdx < files.length; ++fileNdx) { File file = files[fileNdx]; String filename = file.getName(); @@ -285,8 +288,7 @@ public class TaskPersister { synchronized(mService) { final ArrayList<TaskRecord> tasks = mService.mRecentTasks; persistentTaskIds.clear(); - int taskNdx; - for (taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) { + for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) { task = tasks.get(taskNdx); if (DEBUG) Slog.d(TAG, "LazyTaskWriter: task=" + task + " persistable=" + task.isPersistable + " needsPersisting=" + task.needsPersisting); diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java index b94ea62..1b1fc8b 100644 --- a/services/core/java/com/android/server/notification/ManagedServices.java +++ b/services/core/java/com/android/server/notification/ManagedServices.java @@ -259,13 +259,17 @@ abstract public class ManagedServices { userIds[i])); } - ManagedServiceInfo[] toRemove = new ManagedServiceInfo[mServices.size()]; + ArrayList<ManagedServiceInfo> toRemove = new ArrayList<ManagedServiceInfo>(); final SparseArray<ArrayList<ComponentName>> toAdd = new SparseArray<ArrayList<ComponentName>>(); synchronized (mMutex) { - // unbind and remove all existing services - toRemove = mServices.toArray(toRemove); + // Unbind automatically bound services, retain system services. + for (ManagedServiceInfo service : mServices) { + if (!service.isSystem) { + toRemove.add(service); + } + } final ArraySet<ComponentName> newEnabled = new ArraySet<ComponentName>(); final ArraySet<String> newPackages = new ArraySet<String>(); diff --git a/services/core/java/com/android/server/notification/NotificationUsageStats.java b/services/core/java/com/android/server/notification/NotificationUsageStats.java index d81a25e..66cc532 100644 --- a/services/core/java/com/android/server/notification/NotificationUsageStats.java +++ b/services/core/java/com/android/server/notification/NotificationUsageStats.java @@ -46,12 +46,14 @@ import java.util.Map; * {@hide} */ public class NotificationUsageStats { + private static final boolean ENABLE_SQLITE_LOG = false; + // Guarded by synchronized(this). private final Map<String, AggregatedStats> mStats = new HashMap<String, AggregatedStats>(); private final SQLiteLog mSQLiteLog; public NotificationUsageStats(Context context) { - mSQLiteLog = new SQLiteLog(context); + mSQLiteLog = ENABLE_SQLITE_LOG ? new SQLiteLog(context) : null; } /** @@ -63,7 +65,9 @@ public class NotificationUsageStats { for (AggregatedStats stats : getAggregatedStatsLocked(notification)) { stats.numPostedByApp++; } - mSQLiteLog.logPosted(notification); + if (ENABLE_SQLITE_LOG) { + mSQLiteLog.logPosted(notification); + } } /** @@ -85,7 +89,9 @@ public class NotificationUsageStats { stats.numRemovedByApp++; stats.collect(notification.stats); } - mSQLiteLog.logRemoved(notification); + if (ENABLE_SQLITE_LOG) { + mSQLiteLog.logRemoved(notification); + } } /** @@ -97,7 +103,9 @@ public class NotificationUsageStats { stats.numDismissedByUser++; stats.collect(notification.stats); } - mSQLiteLog.logDismissed(notification); + if (ENABLE_SQLITE_LOG) { + mSQLiteLog.logDismissed(notification); + } } /** @@ -108,7 +116,9 @@ public class NotificationUsageStats { for (AggregatedStats stats : getAggregatedStatsLocked(notification)) { stats.numClickedByUser++; } - mSQLiteLog.logClicked(notification); + if (ENABLE_SQLITE_LOG) { + mSQLiteLog.logClicked(notification); + } } /** @@ -164,7 +174,9 @@ public class NotificationUsageStats { for (AggregatedStats as : mStats.values()) { as.dump(pw, indent); } - mSQLiteLog.dump(pw, indent); + if (ENABLE_SQLITE_LOG) { + mSQLiteLog.dump(pw, indent); + } } /** diff --git a/services/core/java/com/android/server/notification/ValidateNotificationPeople.java b/services/core/java/com/android/server/notification/ValidateNotificationPeople.java index 157d749..a629a5f 100644 --- a/services/core/java/com/android/server/notification/ValidateNotificationPeople.java +++ b/services/core/java/com/android/server/notification/ValidateNotificationPeople.java @@ -139,56 +139,64 @@ public class ValidateNotificationPeople implements NotificationSignalExtractor { } private String[] getExtraPeople(Bundle extras) { - String[] people = extras.getStringArray(Notification.EXTRA_PEOPLE); - if (people != null) { - return people; + Object people = extras.get(Notification.EXTRA_PEOPLE); + if (people instanceof String[]) { + return (String[]) people; } - ArrayList<String> stringArray = extras.getStringArrayList(Notification.EXTRA_PEOPLE); - if (stringArray != null) { - return (String[]) stringArray.toArray(); + if (people instanceof ArrayList) { + ArrayList arrayList = (ArrayList) people; + + if (arrayList.isEmpty()) { + return null; + } + + if (arrayList.get(0) instanceof String) { + ArrayList<String> stringArray = (ArrayList<String>) arrayList; + return stringArray.toArray(new String[stringArray.size()]); + } + + if (arrayList.get(0) instanceof CharSequence) { + ArrayList<CharSequence> charSeqList = (ArrayList<CharSequence>) arrayList; + final int N = charSeqList.size(); + String[] array = new String[N]; + for (int i = 0; i < N; i++) { + array[i] = charSeqList.get(i).toString(); + } + return array; + } + + return null; } - String string = extras.getString(Notification.EXTRA_PEOPLE); - if (string != null) { - people = new String[1]; - people[0] = string; - return people; + if (people instanceof String) { + String[] array = new String[1]; + array[0] = (String) people; + return array; } - char[] charArray = extras.getCharArray(Notification.EXTRA_PEOPLE); - if (charArray != null) { - people = new String[1]; - people[0] = new String(charArray); - return people; + + if (people instanceof char[]) { + String[] array = new String[1]; + array[0] = new String((char[]) people); + return array; } - CharSequence charSeq = extras.getCharSequence(Notification.EXTRA_PEOPLE); - if (charSeq != null) { - people = new String[1]; - people[0] = charSeq.toString(); - return people; + if (people instanceof CharSequence) { + String[] array = new String[1]; + array[0] = ((CharSequence) people).toString(); + return array; } - CharSequence[] charSeqArray = extras.getCharSequenceArray(Notification.EXTRA_PEOPLE); - if (charSeqArray != null) { + if (people instanceof CharSequence[]) { + CharSequence[] charSeqArray = (CharSequence[]) people; final int N = charSeqArray.length; - people = new String[N]; + String[] array = new String[N]; for (int i = 0; i < N; i++) { - people[i] = charSeqArray[i].toString(); + array[i] = charSeqArray[i].toString(); } - return people; + return array; } - ArrayList<CharSequence> charSeqList = - extras.getCharSequenceArrayList(Notification.EXTRA_PEOPLE); - if (charSeqList != null) { - final int N = charSeqList.size(); - people = new String[N]; - for (int i = 0; i < N; i++) { - people[i] = charSeqList.get(i).toString(); - } - return people; - } return null; } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index b06b090..d505e81 100755 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -11775,8 +11775,8 @@ public class PackageManagerService extends IPackageManager.Stub { } } if (removed.size() > 0) { - for (int j=0; j<removed.size(); j++) { - PreferredActivity pa = removed.get(i); + for (int r=0; r<removed.size(); r++) { + PreferredActivity pa = removed.get(r); Slog.w(TAG, "Removing dangling preferred activity: " + pa.mPref.mComponent); pir.removeFilter(pa); diff --git a/services/core/java/com/android/server/task/TaskManagerService.java b/services/core/java/com/android/server/task/TaskManagerService.java index 6d208ff..80030b4 100644 --- a/services/core/java/com/android/server/task/TaskManagerService.java +++ b/services/core/java/com/android/server/task/TaskManagerService.java @@ -16,12 +16,22 @@ package com.android.server.task; +import java.io.FileDescriptor; +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.List; + +import android.app.task.ITaskManager; +import android.app.task.Task; import android.content.Context; -import android.content.Task; +import android.content.pm.PackageManager; +import android.os.Binder; import android.os.Handler; import android.os.Looper; import android.os.Message; -import android.util.Log; +import android.os.RemoteException; +import android.os.UserHandle; +import android.util.Slog; import android.util.SparseArray; import com.android.server.task.controllers.TaskStatus; @@ -39,13 +49,6 @@ public class TaskManagerService extends com.android.server.SystemService /** Master list of tasks. */ private final TaskStore mTasks; - /** Check the pending queue and start any tasks. */ - static final int MSG_RUN_PENDING = 0; - /** Initiate the stop task flow. */ - static final int MSG_STOP_TASK = 1; - /** */ - static final int MSG_CHECK_TASKS = 2; - /** * Track Services that have currently active or pending tasks. The index is provided by * {@link TaskStatus#getServiceToken()} @@ -54,6 +57,14 @@ public class TaskManagerService extends com.android.server.SystemService new SparseArray<TaskServiceContext>(); private final TaskHandler mHandler; + private final TaskManagerStub mTaskManagerStub; + + /** Check the pending queue and start any tasks. */ + static final int MSG_RUN_PENDING = 0; + /** Initiate the stop task flow. */ + static final int MSG_STOP_TASK = 1; + /** */ + static final int MSG_CHECK_TASKS = 2; private class TaskHandler extends Handler { @@ -94,21 +105,6 @@ public class TaskManagerService extends com.android.server.SystemService } /** - * Entry point from client to schedule the provided task. - * This will add the task to the - * @param task Task object containing execution parameters - * @param userId The id of the user this task is for. - * @param uId The package identifier of the application this task is for. - * @param canPersistTask Whether or not the client has the appropriate permissions for persisting - * of this task. - * @return Result of this operation. See <code>TaskManager#RESULT_*</code> return codes. - */ - public int schedule(Task task, int userId, int uId, boolean canPersistTask) { - TaskStatus taskStatus = mTasks.addNewTaskForUser(task, userId, uId, canPersistTask); - return 0; - } - - /** * Initializes the system service. * <p> * Subclasses must define a single argument constructor that accepts the context @@ -121,11 +117,42 @@ public class TaskManagerService extends com.android.server.SystemService super(context); mTasks = new TaskStore(context); mHandler = new TaskHandler(context.getMainLooper()); + mTaskManagerStub = new TaskManagerStub(); } @Override public void onStart() { + publishBinderService(Context.TASK_SERVICE, mTaskManagerStub); + } + + /** + * Entry point from client to schedule the provided task. + * This will add the task to the + * @param task Task object containing execution parameters + * @param userId The id of the user this task is for. + * @param uId The package identifier of the application this task is for. + * @param canPersistTask Whether or not the client has the appropriate permissions for + * persisting of this task. + * @return Result of this operation. See <code>TaskManager#RESULT_*</code> return codes. + */ + public int schedule(Task task, int userId, int uId, boolean canPersistTask) { + TaskStatus taskStatus = mTasks.addNewTaskForUser(task, userId, uId, canPersistTask); + return 0; + } + public List<Task> getPendingTasks(int uid) { + ArrayList<Task> outList = new ArrayList<Task>(3); + synchronized (mTasks) { + final SparseArray<TaskStatus> tasks = mTasks.getTasks(); + final int N = tasks.size(); + for (int i = 0; i < N; i++) { + TaskStatus ts = tasks.get(i); + if (ts.getUid() == uid) { + outList.add(ts.getTask()); + } + } + } + return outList; } // StateChangedListener implementations. @@ -162,7 +189,7 @@ public class TaskManagerService extends com.android.server.SystemService public void onTaskCompleted(int serviceToken, int taskId, boolean needsReschedule) { final TaskServiceContext serviceContext = mActiveServices.get(serviceToken); if (serviceContext == null) { - Log.e(TAG, "Task completed for invalid service context; " + serviceToken); + Slog.e(TAG, "Task completed for invalid service context; " + serviceToken); return; } @@ -203,4 +230,98 @@ public class TaskManagerService extends com.android.server.SystemService private void postCheckTasksMessage() { mHandler.obtainMessage(MSG_CHECK_TASKS).sendToTarget(); } + + /** + * Binder stub trampoline implementation + */ + final class TaskManagerStub extends ITaskManager.Stub { + /** Cache determination of whether a given app can persist tasks + * key is uid of the calling app; value is undetermined/true/false + */ + private final SparseArray<Boolean> mPersistCache = new SparseArray<Boolean>(); + + // Determine whether the caller is allowed to persist tasks, with a small cache + // because the lookup is expensive enough that we'd like to avoid repeating it. + // This must be called from within the calling app's binder identity! + private boolean canCallerPersistTasks() { + final boolean canPersist; + final int callingUid = Binder.getCallingUid(); + synchronized (mPersistCache) { + Boolean cached = mPersistCache.get(callingUid); + if (cached) { + canPersist = cached.booleanValue(); + } else { + // Persisting tasks is tantamount to running at boot, so we permit + // it when the app has declared that it uses the RECEIVE_BOOT_COMPLETED + // permission + int result = getContext().checkCallingPermission( + android.Manifest.permission.RECEIVE_BOOT_COMPLETED); + canPersist = (result == PackageManager.PERMISSION_GRANTED); + mPersistCache.put(callingUid, canPersist); + } + } + return canPersist; + } + + // ITaskManager implementation + @Override + public int schedule(Task task) throws RemoteException { + final boolean canPersist = canCallerPersistTasks(); + final int uid = Binder.getCallingUid(); + final int userId = UserHandle.getCallingUserId(); + + long ident = Binder.clearCallingIdentity(); + try { + return TaskManagerService.this.schedule(task, userId, uid, canPersist); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + @Override + public List<Task> getAllPendingTasks() throws RemoteException { + return null; + } + + @Override + public void cancelAll() throws RemoteException { + } + + @Override + public void cancel(int taskId) throws RemoteException { + } + + /** + * "dumpsys" infrastructure + */ + @Override + public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + getContext().enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG); + + long identityToken = Binder.clearCallingIdentity(); + try { + TaskManagerService.this.dumpInternal(pw); + } finally { + Binder.restoreCallingIdentity(identityToken); + } + } + }; + + void dumpInternal(PrintWriter pw) { + synchronized (mTasks) { + pw.print("Registered tasks:"); + if (mTasks.size() > 0) { + SparseArray<TaskStatus> tasks = mTasks.getTasks(); + for (int i = 0; i < tasks.size(); i++) { + TaskStatus ts = tasks.get(i); + pw.println(); + ts.dump(pw, " "); + } + } else { + pw.println(); + pw.println("No tasks scheduled."); + } + } + pw.println(); + } } diff --git a/services/core/java/com/android/server/task/TaskServiceContext.java b/services/core/java/com/android/server/task/TaskServiceContext.java index b51cbb3..165445a 100644 --- a/services/core/java/com/android/server/task/TaskServiceContext.java +++ b/services/core/java/com/android/server/task/TaskServiceContext.java @@ -45,7 +45,7 @@ import java.util.concurrent.atomic.AtomicBoolean; * is reused to start concurrent tasks on the TaskService. Information here is unique * to the service. * Functionality provided by this class: - * - Managages wakelock for the service. + * - Manages wakelock for the service. * - Sends onStartTask() and onStopTask() messages to client app, and handles callbacks. * - */ diff --git a/services/core/java/com/android/server/task/TaskStore.java b/services/core/java/com/android/server/task/TaskStore.java index 3bfc8a5..81187c8 100644 --- a/services/core/java/com/android/server/task/TaskStore.java +++ b/services/core/java/com/android/server/task/TaskStore.java @@ -16,8 +16,8 @@ package com.android.server.task; +import android.app.task.Task; import android.content.Context; -import android.content.Task; import android.util.SparseArray; import com.android.server.task.controllers.TaskStatus; @@ -95,6 +95,13 @@ public class TaskStore { } /** + * @return the number of tasks in the store + */ + public int size() { + return mTasks.size(); + } + + /** * @return The live array of TaskStatus objects. */ public SparseArray<TaskStatus> getTasks() { diff --git a/services/core/java/com/android/server/task/controllers/ConnectivityController.java b/services/core/java/com/android/server/task/controllers/ConnectivityController.java index 6a4e1f3..a0038c5 100644 --- a/services/core/java/com/android/server/task/controllers/ConnectivityController.java +++ b/services/core/java/com/android/server/task/controllers/ConnectivityController.java @@ -70,13 +70,10 @@ public class ConnectivityController extends StateController { } /** - * @param userId Id of the user for whom we are updating the connectivity state. + * */ - private void updateTrackedTasks(int userId) { + private void updateTrackedTasks() { for (TaskStatus ts : mTrackedTasks) { - if (ts.userId != userId) { - continue; - } boolean prevIsConnected = ts.connectivityConstraintSatisfied.getAndSet(mConnectivity); boolean prevIsMetered = ts.meteredConstraintSatisfied.getAndSet(mMetered); if (prevIsConnected != mConnectivity || prevIsMetered != mMetered) { @@ -107,14 +104,13 @@ public class ConnectivityController extends StateController { final NetworkInfo activeNetwork = connManager.getActiveNetworkInfo(); // This broadcast gets sent a lot, only update if the active network has changed. if (activeNetwork.getType() == networkType) { - final int userid = context.getUserId(); mMetered = false; mConnectivity = !intent.getBooleanExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, false); if (mConnectivity) { // No point making the call if we know there's no conn. mMetered = connManager.isActiveNetworkMetered(); } - updateTrackedTasks(userid); + updateTrackedTasks(); } } else { Log.w(TAG, "Unrecognised action in intent: " + action); diff --git a/services/core/java/com/android/server/task/controllers/TaskStatus.java b/services/core/java/com/android/server/task/controllers/TaskStatus.java index d96fedc..d270016 100644 --- a/services/core/java/com/android/server/task/controllers/TaskStatus.java +++ b/services/core/java/com/android/server/task/controllers/TaskStatus.java @@ -16,17 +16,19 @@ package com.android.server.task.controllers; +import android.app.task.Task; import android.content.ComponentName; -import android.content.Task; import android.content.pm.PackageParser; import android.os.Bundle; import android.os.SystemClock; +import android.os.UserHandle; +import java.io.PrintWriter; import java.util.concurrent.atomic.AtomicBoolean; /** * Uniquely identifies a task internally. - * Created from the public {@link android.content.Task} object when it lands on the scheduler. + * Created from the public {@link android.app.task.Task} object when it lands on the scheduler. * Contains current state of the requirements of the task, as well as a function to evaluate * whether it's ready to run. * This object is shared among the various controllers - hence why the different fields are atomic. @@ -36,10 +38,9 @@ import java.util.concurrent.atomic.AtomicBoolean; * @hide */ public class TaskStatus { + final Task task; final int taskId; - final int userId; final int uId; - final ComponentName component; final Bundle extras; final AtomicBoolean chargingConstraintSatisfied = new AtomicBoolean(); @@ -57,9 +58,9 @@ public class TaskStatus { private long earliestRunTimeElapsedMillis; private long latestRunTimeElapsedMillis; - /** Provide a unique handle to the service that this task will be run on. */ + /** Provide a handle to the service that this task will be run on. */ public int getServiceToken() { - return component.hashCode() + userId; + return uId; } /** Generate a TaskStatus object for a given task and uid. */ @@ -70,9 +71,8 @@ public class TaskStatus { /** Set up the state of a newly scheduled task. */ TaskStatus(Task task, int userId, int uId) { + this.task = task; this.taskId = task.getTaskId(); - this.userId = userId; - this.component = task.getService(); this.extras = task.getExtras(); this.uId = uId; @@ -100,16 +100,20 @@ public class TaskStatus { hasConnectivityConstraint = task.getNetworkCapabilities() == Task.NetworkType.ANY; } + public Task getTask() { + return task; + } + public int getTaskId() { return taskId; } public ComponentName getServiceComponent() { - return component; + return task.getService(); } public int getUserId() { - return userId; + return UserHandle.getUserId(uId); } public int getUid() { @@ -161,9 +165,9 @@ public class TaskStatus { @Override public int hashCode() { - int result = component.hashCode(); + int result = getServiceComponent().hashCode(); result = 31 * result + taskId; - result = 31 * result + userId; + result = 31 * result + uId; return result; } @@ -174,7 +178,14 @@ public class TaskStatus { TaskStatus that = (TaskStatus) o; return ((taskId == that.taskId) - && (userId == that.userId) - && (component.equals(that.component))); + && (uId == that.uId) + && (getServiceComponent().equals(that.getServiceComponent()))); + } + + // Dumpsys infrastructure + public void dump(PrintWriter pw, String prefix) { + pw.print(prefix); pw.print("Task "); pw.println(taskId); + pw.print(prefix); pw.print("uid="); pw.println(uId); + pw.print(prefix); pw.print("component="); pw.println(task.getService()); } } diff --git a/services/core/java/com/android/server/trust/TrustAgentWrapper.java b/services/core/java/com/android/server/trust/TrustAgentWrapper.java index 47ce3b6..f18939f 100644 --- a/services/core/java/com/android/server/trust/TrustAgentWrapper.java +++ b/services/core/java/com/android/server/trust/TrustAgentWrapper.java @@ -63,6 +63,11 @@ public class TrustAgentWrapper { public void handleMessage(Message msg) { switch (msg.what) { case MSG_GRANT_TRUST: + if (!isConnected()) { + Log.w(TAG, "Agent is not connected, cannot grant trust: " + + mName.flattenToShortString()); + return; + } mTrusted = true; mMessage = (CharSequence) msg.obj; boolean initiatedByUser = msg.arg1 != 0; @@ -119,6 +124,7 @@ public class TrustAgentWrapper { public void onServiceConnected(ComponentName name, IBinder service) { if (DEBUG) Log.v(TAG, "TrustAgent started : " + name.flattenToString()); mTrustAgentService = ITrustAgentService.Stub.asInterface(service); + mTrustManagerService.mArchive.logAgentConnected(mUserId, name); setCallback(mCallback); } @@ -179,7 +185,10 @@ public class TrustAgentWrapper { public void unbind() { if (DEBUG) Log.v(TAG, "TrustAgent unbound : " + mName.flattenToShortString()); + mTrustManagerService.mArchive.logAgentStopped(mUserId, mName); mContext.unbindService(mConnection); + mTrustAgentService = null; + mHandler.sendEmptyMessage(MSG_REVOKE_TRUST); } public boolean isConnected() { diff --git a/services/core/java/com/android/server/trust/TrustArchive.java b/services/core/java/com/android/server/trust/TrustArchive.java index aad156c..56950d2 100644 --- a/services/core/java/com/android/server/trust/TrustArchive.java +++ b/services/core/java/com/android/server/trust/TrustArchive.java @@ -33,6 +33,8 @@ public class TrustArchive { private static final int TYPE_REVOKE_TRUST = 1; private static final int TYPE_TRUST_TIMEOUT = 2; private static final int TYPE_AGENT_DIED = 3; + private static final int TYPE_AGENT_CONNECTED = 4; + private static final int TYPE_AGENT_STOPPED = 5; private static final int HISTORY_LIMIT = 200; @@ -79,6 +81,14 @@ public class TrustArchive { addEvent(new Event(TYPE_AGENT_DIED, userId, agent, null, 0, false)); } + public void logAgentConnected(int userId, ComponentName agent) { + addEvent(new Event(TYPE_AGENT_CONNECTED, userId, agent, null, 0, false)); + } + + public void logAgentStopped(int userId, ComponentName agent) { + addEvent(new Event(TYPE_AGENT_STOPPED, userId, agent, null, 0, false)); + } + private void addEvent(Event e) { if (mEvents.size() >= HISTORY_LIMIT) { mEvents.removeFirst(); @@ -152,6 +162,10 @@ public class TrustArchive { return "TrustTimeout"; case TYPE_AGENT_DIED: return "AgentDied"; + case TYPE_AGENT_CONNECTED: + return "AgentConnected"; + case TYPE_AGENT_STOPPED: + return "AgentStopped"; default: return "Unknown(" + type + ")"; } diff --git a/services/core/java/com/android/server/tv/TvInputHal.java b/services/core/java/com/android/server/tv/TvInputHal.java new file mode 100644 index 0000000..4bdd2be --- /dev/null +++ b/services/core/java/com/android/server/tv/TvInputHal.java @@ -0,0 +1,128 @@ +/* + * Copyright 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.server.tv; + +import android.os.Handler; +import android.os.HandlerThread; +import android.tv.TvInputHardwareInfo; +import android.tv.TvStreamConfig; +import android.view.Surface; + +/** + * Provides access to the low-level TV input hardware abstraction layer. + */ +final class TvInputHal { + public final static int SUCCESS = 0; + public final static int ERROR_NO_INIT = -1; + public final static int ERROR_STALE_CONFIG = -2; + public final static int ERROR_UNKNOWN = -3; + + public static final int TYPE_HDMI = 1; + public static final int TYPE_BUILT_IN_TUNER = 2; + public static final int TYPE_PASSTHROUGH = 3; + + public interface Callback { + public void onDeviceAvailable( + TvInputHardwareInfo info, TvStreamConfig[] configs); + public void onDeviceUnavailable(int deviceId); + public void onStreamConfigurationChanged(int deviceId, TvStreamConfig[] configs); + } + + private native long nativeOpen(); + + private static native int nativeSetSurface(long ptr, int deviceId, int streamId, + Surface surface); + private static native TvStreamConfig[] nativeGetStreamConfigs(long ptr, int deviceId, + int generation); + private static native void nativeClose(long ptr); + + private long mPtr = 0l; + private final Callback mCallback; + private final HandlerThread mThread = new HandlerThread("TV input HAL event thread"); + private final Handler mHandler; + private int mStreamConfigGeneration = 0; + private TvStreamConfig[] mStreamConfigs; + + public TvInputHal(Callback callback) { + mCallback = callback; + mThread.start(); + mHandler = new Handler(mThread.getLooper()); + } + + public void init() { + mPtr = nativeOpen(); + } + + public int setSurface(int deviceId, Surface surface, TvStreamConfig streamConfig) { + if (mPtr == 0) { + return ERROR_NO_INIT; + } + if (mStreamConfigGeneration != streamConfig.getGeneration()) { + return ERROR_STALE_CONFIG; + } + if (nativeSetSurface(mPtr, deviceId, streamConfig.getStreamId(), surface) == 0) { + return SUCCESS; + } else { + return ERROR_UNKNOWN; + } + } + + public void close() { + if (mPtr != 0l) { + nativeClose(mPtr); + mThread.quitSafely(); + } + } + + private synchronized void retrieveStreamConfigs(int deviceId) { + ++mStreamConfigGeneration; + mStreamConfigs = nativeGetStreamConfigs(mPtr, deviceId, mStreamConfigGeneration); + } + + // Called from native + private void deviceAvailableFromNative(int deviceId, int type) { + final TvInputHardwareInfo info = new TvInputHardwareInfo(deviceId, type); + mHandler.post(new Runnable() { + @Override + public void run() { + retrieveStreamConfigs(info.getDeviceId()); + mCallback.onDeviceAvailable(info, mStreamConfigs); + } + }); + } + + private void deviceUnavailableFromNative(int deviceId) { + final int id = deviceId; + mHandler.post(new Runnable() { + @Override + public void run() { + mCallback.onDeviceUnavailable(id); + } + }); + } + + private void streamConfigsChangedFromNative(int deviceId) { + final int id = deviceId; + mHandler.post(new Runnable() { + @Override + public void run() { + retrieveStreamConfigs(id); + mCallback.onStreamConfigurationChanged(id, mStreamConfigs); + } + }); + } +} diff --git a/services/core/java/com/android/server/tv/TvInputHardwareManager.java b/services/core/java/com/android/server/tv/TvInputHardwareManager.java new file mode 100644 index 0000000..b95b0f0 --- /dev/null +++ b/services/core/java/com/android/server/tv/TvInputHardwareManager.java @@ -0,0 +1,308 @@ +/* + * 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.server.tv; + +import android.content.Context; +import android.os.IBinder; +import android.os.RemoteException; +import android.tv.ITvInputHardware; +import android.tv.ITvInputHardwareCallback; +import android.tv.TvInputHardwareInfo; +import android.tv.TvStreamConfig; +import android.util.Slog; +import android.util.SparseArray; +import android.view.KeyEvent; +import android.view.Surface; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * A helper class for TvInputManagerService to handle TV input hardware. + * + * This class does a basic connection management and forwarding calls to TvInputHal which eventually + * calls to tv_input HAL module. + * + * @hide + */ +class TvInputHardwareManager implements TvInputHal.Callback { + private static final String TAG = TvInputHardwareManager.class.getSimpleName(); + private final TvInputHal mHal = new TvInputHal(this); + private final SparseArray<Connection> mConnections = new SparseArray<Connection>(); + private final List<TvInputHardwareInfo> mInfoList = new ArrayList<TvInputHardwareInfo>(); + private final Context mContext; + private final Set<Integer> mActiveHdmiSources = new HashSet<Integer>(); + + private final Object mLock = new Object(); + + public TvInputHardwareManager(Context context) { + mContext = context; + // TODO(hdmi): mHdmiManager = mContext.getSystemService(...); + // TODO(hdmi): mHdmiClient = mHdmiManager.getTvClient(); + mHal.init(); + } + + @Override + public void onDeviceAvailable( + TvInputHardwareInfo info, TvStreamConfig[] configs) { + synchronized (mLock) { + Connection connection = new Connection(info); + connection.updateConfigsLocked(configs); + mConnections.put(info.getDeviceId(), connection); + buildInfoListLocked(); + // TODO: notify if necessary + } + } + + private void buildInfoListLocked() { + mInfoList.clear(); + for (int i = 0; i < mConnections.size(); ++i) { + mInfoList.add(mConnections.valueAt(i).getInfoLocked()); + } + } + + @Override + public void onDeviceUnavailable(int deviceId) { + synchronized (mLock) { + Connection connection = mConnections.get(deviceId); + if (connection == null) { + Slog.e(TAG, "onDeviceUnavailable: Cannot find a connection with " + deviceId); + return; + } + connection.resetLocked(null, null, null, null); + mConnections.remove(deviceId); + buildInfoListLocked(); + // TODO: notify if necessary + } + } + + @Override + public void onStreamConfigurationChanged(int deviceId, TvStreamConfig[] configs) { + synchronized (mLock) { + Connection connection = mConnections.get(deviceId); + if (connection == null) { + Slog.e(TAG, "StreamConfigurationChanged: Cannot find a connection with " + + deviceId); + return; + } + connection.updateConfigsLocked(configs); + try { + connection.getCallbackLocked().onStreamConfigChanged(configs); + } catch (RemoteException e) { + Slog.e(TAG, "onStreamConfigurationChanged: " + e); + } + } + } + + public List<TvInputHardwareInfo> getHardwareList() { + synchronized (mLock) { + return mInfoList; + } + } + + /** + * Create a TvInputHardware object with a specific deviceId. One service at a time can access + * the object, and if more than one process attempts to create hardware with the same deviceId, + * the latest service will get the object and all the other hardware are released. The + * release is notified via ITvInputHardwareCallback.onReleased(). + */ + public ITvInputHardware acquireHardware(int deviceId, ITvInputHardwareCallback callback, + int callingUid, int resolvedUserId) { + if (callback == null) { + throw new NullPointerException(); + } + synchronized (mLock) { + Connection connection = mConnections.get(deviceId); + if (connection == null) { + Slog.e(TAG, "Invalid deviceId : " + deviceId); + return null; + } + if (connection.getCallingUidLocked() != callingUid + || connection.getResolvedUserIdLocked() != resolvedUserId) { + TvInputHardwareImpl hardware = new TvInputHardwareImpl(connection.getInfoLocked()); + try { + callback.asBinder().linkToDeath(connection, 0); + } catch (RemoteException e) { + hardware.release(); + return null; + } + connection.resetLocked(hardware, callback, callingUid, resolvedUserId); + } + return connection.getHardwareLocked(); + } + } + + /** + * Release the specified hardware. + */ + public void releaseHardware(int deviceId, ITvInputHardware hardware, int callingUid, + int resolvedUserId) { + synchronized (mLock) { + Connection connection = mConnections.get(deviceId); + if (connection == null) { + Slog.e(TAG, "Invalid deviceId : " + deviceId); + return; + } + if (connection.getHardwareLocked() != hardware + || connection.getCallingUidLocked() != callingUid + || connection.getResolvedUserIdLocked() != resolvedUserId) { + return; + } + connection.resetLocked(null, null, null, null); + } + } + + private class Connection implements IBinder.DeathRecipient { + private final TvInputHardwareInfo mInfo; + private TvInputHardwareImpl mHardware = null; + private ITvInputHardwareCallback mCallback; + private TvStreamConfig[] mConfigs = null; + private Integer mCallingUid = null; + private Integer mResolvedUserId = null; + + public Connection(TvInputHardwareInfo info) { + mInfo = info; + } + + // *Locked methods assume TvInputHardwareManager.mLock is held. + + public void resetLocked(TvInputHardwareImpl hardware, + ITvInputHardwareCallback callback, Integer callingUid, Integer resolvedUserId) { + if (mHardware != null) { + try { + mCallback.onReleased(); + } catch (RemoteException e) { + Slog.e(TAG, "Connection::resetHardware: " + e); + } + mHardware.release(); + } + mHardware = hardware; + mCallback = callback; + mCallingUid = callingUid; + mResolvedUserId = resolvedUserId; + + if (mHardware != null && mCallback != null) { + try { + mCallback.onStreamConfigChanged(getConfigsLocked()); + } catch (RemoteException e) { + Slog.e(TAG, "Connection::resetHardware: " + e); + } + } + } + + public void updateConfigsLocked(TvStreamConfig[] configs) { + mConfigs = configs; + } + + public TvInputHardwareInfo getInfoLocked() { + return mInfo; + } + + public ITvInputHardware getHardwareLocked() { + return mHardware; + } + + public ITvInputHardwareCallback getCallbackLocked() { + return mCallback; + } + + public TvStreamConfig[] getConfigsLocked() { + return mConfigs; + } + + public int getCallingUidLocked() { + return mCallingUid; + } + + public int getResolvedUserIdLocked() { + return mResolvedUserId; + } + + @Override + public void binderDied() { + synchronized (mLock) { + resetLocked(null, null, null, null); + } + } + } + + private class TvInputHardwareImpl extends ITvInputHardware.Stub { + private final TvInputHardwareInfo mInfo; + private boolean mReleased = false; + private final Object mImplLock = new Object(); + + public TvInputHardwareImpl(TvInputHardwareInfo info) { + mInfo = info; + } + + public void release() { + synchronized (mImplLock) { + mReleased = true; + } + } + + @Override + public boolean setSurface(Surface surface, TvStreamConfig config) + throws RemoteException { + synchronized (mImplLock) { + if (mReleased) { + throw new IllegalStateException("Device already released."); + } + if (mInfo.getType() == TvInputHal.TYPE_HDMI) { + if (surface != null) { + // Set "Active Source" for HDMI. + // TODO(hdmi): mHdmiClient.deviceSelect(...); + mActiveHdmiSources.add(mInfo.getDeviceId()); + } else { + mActiveHdmiSources.remove(mInfo.getDeviceId()); + if (mActiveHdmiSources.size() == 0) { + // Tell HDMI that no HDMI source is active + // TODO(hdmi): mHdmiClient.portSelect(null); + } + } + } + return mHal.setSurface(mInfo.getDeviceId(), surface, config) == TvInputHal.SUCCESS; + } + } + + @Override + public void setVolume(float volume) throws RemoteException { + synchronized (mImplLock) { + if (mReleased) { + throw new IllegalStateException("Device already released."); + } + } + // TODO + } + + @Override + public boolean dispatchKeyEventToHdmi(KeyEvent event) throws RemoteException { + synchronized (mImplLock) { + if (mReleased) { + throw new IllegalStateException("Device already released."); + } + } + if (mInfo.getType() != TvInputHal.TYPE_HDMI) { + return false; + } + // TODO(hdmi): mHdmiClient.sendKeyEvent(event); + return false; + } + } +} diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java index 8ad7fff..6c38a4c 100644 --- a/services/core/java/com/android/server/tv/TvInputManagerService.java +++ b/services/core/java/com/android/server/tv/TvInputManagerService.java @@ -42,11 +42,14 @@ import android.os.RemoteException; import android.os.UserHandle; import android.provider.TvContract; import android.tv.ITvInputClient; +import android.tv.ITvInputHardware; +import android.tv.ITvInputHardwareCallback; import android.tv.ITvInputManager; import android.tv.ITvInputService; import android.tv.ITvInputServiceCallback; import android.tv.ITvInputSession; import android.tv.ITvInputSessionCallback; +import android.tv.TvInputHardwareInfo; import android.tv.TvInputInfo; import android.tv.TvInputService; import android.util.Slog; @@ -71,6 +74,7 @@ public final class TvInputManagerService extends SystemService { private static final String TAG = "TvInputManagerService"; private final Context mContext; + private final TvInputHardwareManager mTvInputHardwareManager; private final ContentResolver mContentResolver; @@ -92,6 +96,7 @@ public final class TvInputManagerService extends SystemService { mContentResolver = context.getContentResolver(); mLogHandler = new LogHandler(IoThread.get().getLooper()); + mTvInputHardwareManager = new TvInputHardwareManager(context); registerBroadcastReceivers(); synchronized (mLock) { @@ -730,6 +735,64 @@ public final class TvInputManagerService extends SystemService { Binder.restoreCallingIdentity(identity); } } + + @Override + public List<TvInputHardwareInfo> getHardwareList() throws RemoteException { + if (mContext.checkCallingPermission( + android.Manifest.permission.TV_INPUT_HARDWARE) + != PackageManager.PERMISSION_GRANTED) { + return null; + } + + final long identity = Binder.clearCallingIdentity(); + try { + return mTvInputHardwareManager.getHardwareList(); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + + @Override + public ITvInputHardware acquireTvInputHardware(int deviceId, + ITvInputHardwareCallback callback, int userId) throws RemoteException { + if (mContext.checkCallingPermission( + android.Manifest.permission.TV_INPUT_HARDWARE) + != PackageManager.PERMISSION_GRANTED) { + return null; + } + + final long identity = Binder.clearCallingIdentity(); + final int callingUid = Binder.getCallingUid(); + final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, + userId, "acquireTvInputHardware"); + try { + return mTvInputHardwareManager.acquireHardware( + deviceId, callback, callingUid, resolvedUserId); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + + @Override + public void releaseTvInputHardware(int deviceId, ITvInputHardware hardware, int userId) + throws RemoteException { + if (mContext.checkCallingPermission( + android.Manifest.permission.TV_INPUT_HARDWARE) + != PackageManager.PERMISSION_GRANTED) { + return; + } + + final long identity = Binder.clearCallingIdentity(); + final int callingUid = Binder.getCallingUid(); + final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, + userId, "releaseTvInputHardware"); + try { + mTvInputHardwareManager.releaseHardware( + deviceId, hardware, callingUid, resolvedUserId); + } finally { + Binder.restoreCallingIdentity(identity); + } + } } private static final class UserState { diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java index e2d2ac6..4f8b9d7 100644 --- a/services/core/java/com/android/server/wm/AppTransition.java +++ b/services/core/java/com/android/server/wm/AppTransition.java @@ -24,9 +24,7 @@ import android.graphics.Rect; import android.os.Debug; import android.os.Handler; import android.os.IRemoteCallback; -import android.os.SystemProperties; import android.util.Slog; -import android.view.View; import android.view.WindowManager; import android.view.animation.AlphaAnimation; import android.view.animation.Animation; @@ -299,7 +297,7 @@ public class AppTransition implements Dump { return null; } - Animation loadAnimation(WindowManager.LayoutParams lp, int animAttr) { + Animation loadAnimationAttr(WindowManager.LayoutParams lp, int animAttr) { int anim = 0; Context context = mContext; if (animAttr >= 0) { @@ -315,7 +313,19 @@ public class AppTransition implements Dump { return null; } - private Animation loadAnimation(String packageName, int resId) { + Animation loadAnimationRes(WindowManager.LayoutParams lp, int resId) { + Context context = mContext; + if (resId >= 0) { + AttributeCache.Entry ent = getCachedAnimations(lp); + if (ent != null) { + context = ent.context; + } + return AnimationUtils.loadAnimation(context, resId); + } + return null; + } + + private Animation loadAnimationRes(String packageName, int resId) { int anim = 0; Context context = mContext; if (resId >= 0) { @@ -695,11 +705,31 @@ public class AppTransition implements Dump { Animation loadAnimation(WindowManager.LayoutParams lp, int transit, boolean enter, - int appWidth, int appHeight, int orientation, - Rect containingFrame, Rect contentInsets, boolean isFullScreen) { + int appWidth, int appHeight, int orientation, Rect containingFrame, Rect contentInsets, + boolean isFullScreen, boolean isVoiceInteraction) { Animation a; - if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_CUSTOM) { - a = loadAnimation(mNextAppTransitionPackage, enter ? + if (isVoiceInteraction && (transit == TRANSIT_ACTIVITY_OPEN + || transit == TRANSIT_TASK_OPEN + || transit == TRANSIT_TASK_TO_FRONT)) { + a = loadAnimationRes(lp, enter + ? com.android.internal.R.anim.voice_activity_open_enter + : com.android.internal.R.anim.voice_activity_open_exit); + if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG, + "applyAnimation voice:" + + " anim=" + a + " transit=" + transit + " isEntrance=" + enter + + " Callers=" + Debug.getCallers(3)); + } else if (isVoiceInteraction && (transit == TRANSIT_ACTIVITY_CLOSE + || transit == TRANSIT_TASK_CLOSE + || transit == TRANSIT_TASK_TO_BACK)) { + a = loadAnimationRes(lp, enter + ? com.android.internal.R.anim.voice_activity_close_enter + : com.android.internal.R.anim.voice_activity_close_exit); + if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG, + "applyAnimation voice:" + + " anim=" + a + " transit=" + transit + " isEntrance=" + enter + + " Callers=" + Debug.getCallers(3)); + } else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_CUSTOM) { + a = loadAnimationRes(mNextAppTransitionPackage, enter ? mNextAppTransitionEnter : mNextAppTransitionExit); if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG, "applyAnimation:" @@ -782,7 +812,7 @@ public class AppTransition implements Dump { : WindowAnimation_wallpaperIntraCloseExitAnimation; break; } - a = animAttr != 0 ? loadAnimation(lp, animAttr) : null; + a = animAttr != 0 ? loadAnimationAttr(lp, animAttr) : null; if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG, "applyAnimation:" + " anim=" + a diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java index ca4ad8a..12c15e2 100644 --- a/services/core/java/com/android/server/wm/AppWindowToken.java +++ b/services/core/java/com/android/server/wm/AppWindowToken.java @@ -50,6 +50,8 @@ class AppWindowToken extends WindowToken { final WindowAnimator mAnimator; + final boolean voiceInteraction; + int groupId = -1; boolean appFullscreen; int requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; @@ -107,11 +109,13 @@ class AppWindowToken extends WindowToken { boolean mDeferRemoval; - AppWindowToken(WindowManagerService _service, IApplicationToken _token) { + AppWindowToken(WindowManagerService _service, IApplicationToken _token, + boolean _voiceInteraction) { super(_service, _token.asBinder(), WindowManager.LayoutParams.TYPE_APPLICATION, true); appWindowToken = this; appToken = _token; + voiceInteraction = _voiceInteraction; mInputApplicationHandle = new InputApplicationHandle(this); mAnimator = service.mAnimator; mAppAnimator = new AppWindowAnimator(this); @@ -249,7 +253,7 @@ class AppWindowToken extends WindowToken { void dump(PrintWriter pw, String prefix) { super.dump(pw, prefix); if (appToken != null) { - pw.print(prefix); pw.println("app=true"); + pw.print(prefix); pw.print("app=true voiceInteraction="); pw.println(voiceInteraction); } if (allAppWindows.size() > 0) { pw.print(prefix); pw.print("allAppWindows="); pw.println(allAppWindows); diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java index 266527d..6fdd535 100644 --- a/services/core/java/com/android/server/wm/WindowAnimator.java +++ b/services/core/java/com/android/server/wm/WindowAnimator.java @@ -36,6 +36,7 @@ import android.util.TimeUtils; import android.view.Display; import android.view.SurfaceControl; import android.view.WindowManagerPolicy; +import android.view.animation.AlphaAnimation; import android.view.animation.Animation; import com.android.server.wm.WindowManagerService.LayoutFields; @@ -50,6 +51,9 @@ import java.util.ArrayList; public class WindowAnimator { private static final String TAG = "WindowAnimator"; + /** How long to give statusbar to clear the private keyguard flag when animating out */ + private static final long KEYGUARD_ANIM_TIMEOUT_MS = 1000; + final WindowManagerService mService; final Context mContext; final WindowManagerPolicy mPolicy; @@ -82,6 +86,8 @@ public class WindowAnimator { boolean mInitialized = false; + boolean mKeyguardGoingAway; + // forceHiding states. static final int KEYGUARD_NOT_SHOWN = 0; static final int KEYGUARD_ANIMATING_IN = 1; @@ -213,6 +219,29 @@ public class WindowAnimator { final WindowList windows = mService.getWindowListLocked(displayId); ArrayList<WindowStateAnimator> unForceHiding = null; boolean wallpaperInUnForceHiding = false; + + if (mKeyguardGoingAway) { + for (int i = windows.size() - 1; i >= 0; i--) { + WindowState win = windows.get(i); + if (!mPolicy.isKeyguardHostWindow(win.mAttrs)) { + continue; + } + final WindowStateAnimator winAnimator = win.mWinAnimator; + if (mPolicy.doesForceHide(win.mAttrs)) { + if (!winAnimator.mAnimating) { + // Create a new animation to delay until keyguard is gone on its own. + winAnimator.mAnimation = new AlphaAnimation(1.0f, 1.0f); + winAnimator.mAnimation.setDuration(KEYGUARD_ANIM_TIMEOUT_MS); + winAnimator.mAnimationIsEntrance = false; + } + } else { + mKeyguardGoingAway = false; + winAnimator.clearAnimation(); + } + break; + } + } + mForceHiding = KEYGUARD_NOT_SHOWN; for (int i = windows.size() - 1; i >= 0; i--) { @@ -239,7 +268,7 @@ public class WindowAnimator { } } - if (mPolicy.doesForceHide(win, win.mAttrs)) { + if (mPolicy.doesForceHide(win.mAttrs)) { if (!wasAnimating && nowAnimating) { if (WindowManagerService.DEBUG_ANIM || WindowManagerService.DEBUG_VISIBILITY) Slog.v(TAG, @@ -252,6 +281,11 @@ public class WindowAnimator { getPendingLayoutChanges(displayId)); } mService.mFocusMayChange = true; + } else if (mKeyguardGoingAway && !nowAnimating) { + // Timeout!! + Slog.e(TAG, "Timeout waiting for animation to startup"); + mPolicy.startKeyguardExitAnimation(0); + mKeyguardGoingAway = false; } if (win.isReadyForDisplay()) { if (nowAnimating) { @@ -265,7 +299,7 @@ public class WindowAnimator { } } if (WindowManagerService.DEBUG_VISIBILITY) Slog.v(TAG, - "Force hide " + mForceHiding + "Force hide " + forceHidingToString() + " hasSurface=" + win.mHasSurface + " policyVis=" + win.mPolicyVisibility + " destroying=" + win.mDestroying @@ -349,12 +383,18 @@ public class WindowAnimator { // If we have windows that are being show due to them no longer // being force-hidden, apply the appropriate animation to them. if (unForceHiding != null) { + boolean startKeyguardExit = true; for (int i=unForceHiding.size()-1; i>=0; i--) { Animation a = mPolicy.createForceHideEnterAnimation(wallpaperInUnForceHiding); if (a != null) { final WindowStateAnimator winAnimator = unForceHiding.get(i); winAnimator.setAnimation(a); winAnimator.mAnimationIsEntrance = true; + if (startKeyguardExit) { + // Do one time only. + mPolicy.startKeyguardExitAnimation(a.getStartOffset()); + startKeyguardExit = false; + } } } } diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 1a0dd82..7382f4c 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -2215,6 +2215,11 @@ public class WindowManagerService extends IWindowManager.Stub + attrs.token + ". Aborting."); return WindowManagerGlobal.ADD_BAD_APP_TOKEN; } + if (type == TYPE_VOICE_INTERACTION) { + Slog.w(TAG, "Attempted to add voice interaction window with unknown token " + + attrs.token + ". Aborting."); + return WindowManagerGlobal.ADD_BAD_APP_TOKEN; + } if (type == TYPE_WALLPAPER) { Slog.w(TAG, "Attempted to add wallpaper window with unknown token " + attrs.token + ". Aborting."); @@ -2250,6 +2255,12 @@ public class WindowManagerService extends IWindowManager.Stub + attrs.token + ". Aborting."); return WindowManagerGlobal.ADD_BAD_APP_TOKEN; } + } else if (type == TYPE_VOICE_INTERACTION) { + if (token.windowType != TYPE_VOICE_INTERACTION) { + Slog.w(TAG, "Attempted to add voice interaction window with bad token " + + attrs.token + ". Aborting."); + return WindowManagerGlobal.ADD_BAD_APP_TOKEN; + } } else if (type == TYPE_WALLPAPER) { if (token.windowType != TYPE_WALLPAPER) { Slog.w(TAG, "Attempted to add wallpaper window with bad token " @@ -3173,7 +3184,7 @@ public class WindowManagerService extends IWindowManager.Stub } private boolean applyAnimationLocked(AppWindowToken atoken, - WindowManager.LayoutParams lp, int transit, boolean enter) { + WindowManager.LayoutParams lp, int transit, boolean enter, boolean isVoiceInteraction) { // Only apply an animation if the display isn't frozen. If it is // frozen, there is no reason to animate and it can cause strange // artifacts when we unfreeze the display if some different animation @@ -3189,6 +3200,7 @@ public class WindowManagerService extends IWindowManager.Stub WindowState win = atoken.findMainWindow(); Rect containingFrame = new Rect(0, 0, width, height); Rect contentInsets = new Rect(); + boolean isFullScreen = true; if (win != null) { if (win.mContainingFrame != null) { containingFrame.set(win.mContainingFrame); @@ -3196,13 +3208,14 @@ public class WindowManagerService extends IWindowManager.Stub if (win.mContentInsets != null) { contentInsets.set(win.mContentInsets); } + isFullScreen = + ((win.mSystemUiVisibility & SYSTEM_UI_FLAGS_LAYOUT_STABLE_FULLSCREEN) == + SYSTEM_UI_FLAGS_LAYOUT_STABLE_FULLSCREEN); } - boolean isFullScreen = - ((win.mSystemUiVisibility & SYSTEM_UI_FLAGS_LAYOUT_STABLE_FULLSCREEN) - == SYSTEM_UI_FLAGS_LAYOUT_STABLE_FULLSCREEN); Animation a = mAppTransition.loadAnimation(lp, transit, enter, width, height, - mCurConfiguration.orientation, containingFrame, contentInsets, isFullScreen); + mCurConfiguration.orientation, containingFrame, contentInsets, isFullScreen, + isVoiceInteraction); if (a != null) { if (DEBUG_ANIM) { RuntimeException e = null; @@ -3422,7 +3435,7 @@ public class WindowManagerService extends IWindowManager.Stub @Override public void addAppToken(int addPos, IApplicationToken token, int taskId, int stackId, int requestedOrientation, boolean fullscreen, boolean showWhenLocked, int userId, - int configChanges) { + int configChanges, boolean voiceInteraction) { if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS, "addAppToken()")) { throw new SecurityException("Requires MANAGE_APP_TOKENS permission"); @@ -3448,7 +3461,7 @@ public class WindowManagerService extends IWindowManager.Stub Slog.w(TAG, "Attempted to add existing app token: " + token); return; } - atoken = new AppWindowToken(this, token); + atoken = new AppWindowToken(this, token, voiceInteraction); atoken.inputDispatchingTimeoutNanos = inputDispatchingTimeoutNanos; atoken.groupId = taskId; atoken.appFullscreen = fullscreen; @@ -4200,7 +4213,7 @@ public class WindowManagerService extends IWindowManager.Stub } boolean setTokenVisibilityLocked(AppWindowToken wtoken, WindowManager.LayoutParams lp, - boolean visible, int transit, boolean performLayout) { + boolean visible, int transit, boolean performLayout, boolean isVoiceInteraction) { boolean delayed = false; if (wtoken.clientHidden == visible) { @@ -4221,7 +4234,7 @@ public class WindowManagerService extends IWindowManager.Stub if (wtoken.mAppAnimator.animation == AppWindowAnimator.sDummyAnimation) { wtoken.mAppAnimator.animation = null; } - if (applyAnimationLocked(wtoken, lp, transit, visible)) { + if (applyAnimationLocked(wtoken, lp, transit, visible, isVoiceInteraction)) { delayed = runningAppAnimation = true; } WindowState window = wtoken.findMainWindow(); @@ -4399,7 +4412,7 @@ public class WindowManagerService extends IWindowManager.Stub final long origId = Binder.clearCallingIdentity(); setTokenVisibilityLocked(wtoken, null, visible, AppTransition.TRANSIT_UNSET, - true); + true, wtoken.voiceInteraction); wtoken.updateReportedVisibilityLocked(); Binder.restoreCallingIdentity(origId); } @@ -4546,7 +4559,7 @@ public class WindowManagerService extends IWindowManager.Stub if (basewtoken != null && (wtoken=basewtoken.appWindowToken) != null) { if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Removing app token: " + wtoken); delayed = setTokenVisibilityLocked(wtoken, null, false, - AppTransition.TRANSIT_UNSET, true); + AppTransition.TRANSIT_UNSET, true, wtoken.voiceInteraction); wtoken.inPendingTransaction = false; mOpeningApps.remove(wtoken); wtoken.waitingToShow = false; @@ -5126,6 +5139,18 @@ public class WindowManagerService extends IWindowManager.Stub } @Override + public void keyguardGoingAway() { + if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DISABLE_KEYGUARD) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("Requires DISABLE_KEYGUARD permission"); + } + synchronized (mWindowMap) { + mAnimator.mKeyguardGoingAway = true; + requestTraversalLocked(); + } + } + + @Override public void closeSystemDialogs(String reason) { synchronized(mWindowMap) { final int numDisplays = mDisplayContents.size(); @@ -7148,9 +7173,7 @@ public class WindowManagerService extends IWindowManager.Stub public static final int TAP_OUTSIDE_STACK = 31; public static final int NOTIFY_ACTIVITY_DRAWN = 32; - public static final int REMOVE_STARTING_TIMEOUT = 33; - - public static final int SHOW_DISPLAY_MASK = 34; + public static final int SHOW_DISPLAY_MASK = 33; @Override public void handleMessage(Message msg) { @@ -8527,6 +8550,7 @@ public class WindowManagerService extends IWindowManager.Stub LayoutParams animLp = null; int bestAnimLayer = -1; boolean fullscreenAnim = false; + boolean voiceInteraction = false; if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "New wallpaper target=" + mWallpaperTarget @@ -8571,6 +8595,8 @@ public class WindowManagerService extends IWindowManager.Stub } } + voiceInteraction |= wtoken.voiceInteraction; + if (wtoken.appFullscreen) { WindowState ws = wtoken.findMainWindow(); if (ws != null) { @@ -8643,7 +8669,7 @@ public class WindowManagerService extends IWindowManager.Stub appAnimator.clearThumbnail(); wtoken.inPendingTransaction = false; appAnimator.animation = null; - setTokenVisibilityLocked(wtoken, animLp, true, transit, false); + setTokenVisibilityLocked(wtoken, animLp, true, transit, false, voiceInteraction); wtoken.updateReportedVisibilityLocked(); wtoken.waitingToShow = false; @@ -8675,7 +8701,7 @@ public class WindowManagerService extends IWindowManager.Stub wtoken.mAppAnimator.clearThumbnail(); wtoken.inPendingTransaction = false; wtoken.mAppAnimator.animation = null; - setTokenVisibilityLocked(wtoken, animLp, false, transit, false); + setTokenVisibilityLocked(wtoken, animLp, false, transit, false, voiceInteraction); wtoken.updateReportedVisibilityLocked(); wtoken.waitingToHide = false; // Force the allDrawn flag, because we want to start diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index c88382c..4a80e3e 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -714,6 +714,11 @@ final class WindowState implements WindowManagerPolicy.WindowState { return mAppToken != null ? mAppToken.appToken : null; } + @Override + public boolean isVoiceInteraction() { + return mAppToken != null ? mAppToken.voiceInteraction : false; + } + boolean setInsetsChanged() { mOverscanInsetsChanged |= !mLastOverscanInsets.equals(mOverscanInsets); mContentInsetsChanged |= !mLastContentInsets.equals(mContentInsets); diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java index 1e79dcb..e257ebc 100644 --- a/services/core/java/com/android/server/wm/WindowStateAnimator.java +++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java @@ -1658,7 +1658,7 @@ class WindowStateAnimator { break; } if (attr >= 0) { - a = mService.mAppTransition.loadAnimation(mWin.mAttrs, attr); + a = mService.mAppTransition.loadAnimationAttr(mWin.mAttrs, attr); } } if (WindowManagerService.DEBUG_ANIM) Slog.v(TAG, diff --git a/services/core/jni/Android.mk b/services/core/jni/Android.mk index 51583a5..3cfb45b 100644 --- a/services/core/jni/Android.mk +++ b/services/core/jni/Android.mk @@ -23,6 +23,7 @@ LOCAL_SRC_FILES += \ $(LOCAL_REL_DIR)/com_android_server_power_PowerManagerService.cpp \ $(LOCAL_REL_DIR)/com_android_server_SerialService.cpp \ $(LOCAL_REL_DIR)/com_android_server_SystemServer.cpp \ + $(LOCAL_REL_DIR)/com_android_server_tv_TvInputHal.cpp \ $(LOCAL_REL_DIR)/com_android_server_UsbDeviceManager.cpp \ $(LOCAL_REL_DIR)/com_android_server_UsbHostManager.cpp \ $(LOCAL_REL_DIR)/com_android_server_VibratorService.cpp \ diff --git a/services/core/jni/com_android_server_tv_TvInputHal.cpp b/services/core/jni/com_android_server_tv_TvInputHal.cpp new file mode 100644 index 0000000..f0c4f3a --- /dev/null +++ b/services/core/jni/com_android_server_tv_TvInputHal.cpp @@ -0,0 +1,388 @@ +/* + * Copyright 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. + */ + +#define LOG_TAG "TvInputHal" + +//#define LOG_NDEBUG 0 + +#include "android_runtime/AndroidRuntime.h" +#include "android_runtime/android_view_Surface.h" +#include "JNIHelp.h" +#include "jni.h" + +#include <gui/Surface.h> +#include <utils/Errors.h> +#include <utils/KeyedVector.h> +#include <utils/Log.h> +#include <utils/NativeHandle.h> +#include <hardware/tv_input.h> + +namespace android { + +static struct { + jmethodID deviceAvailable; + jmethodID deviceUnavailable; + jmethodID streamConfigsChanged; +} gTvInputHalClassInfo; + +static struct { + jclass clazz; +} gTvStreamConfigClassInfo; + +static struct { + jclass clazz; + + jmethodID constructor; + jmethodID streamId; + jmethodID type; + jmethodID maxWidth; + jmethodID maxHeight; + jmethodID generation; + jmethodID build; +} gTvStreamConfigBuilderClassInfo; + +//////////////////////////////////////////////////////////////////////////////// + +class JTvInputHal { +public: + ~JTvInputHal(); + + static JTvInputHal* createInstance(JNIEnv* env, jobject thiz); + + int setSurface(int deviceId, int streamId, const sp<Surface>& surface); + void getStreamConfigs(int deviceId, jobjectArray* array); + const tv_stream_config_t* getStreamConfigs(int deviceId, int* numConfigs); + +private: + class Connection { + public: + Connection() : mStreamId(0) {} + + sp<Surface> mSurface; + sp<NativeHandle> mSourceHandle; + int mStreamId; + }; + + JTvInputHal(JNIEnv* env, jobject thiz, tv_input_device_t* dev); + + static void notify( + tv_input_device_t* dev,tv_input_event_t* event, void* data); + + void onDeviceAvailable(const tv_input_device_info_t& info); + void onDeviceUnavailable(int deviceId); + void onStreamConfigurationsChanged(int deviceId); + + jweak mThiz; + tv_input_device_t* mDevice; + tv_input_callback_ops_t mCallback; + + KeyedVector<int, Connection> mConnections; +}; + +JTvInputHal::JTvInputHal(JNIEnv* env, jobject thiz, tv_input_device_t* device) { + mThiz = env->NewWeakGlobalRef(thiz); + mDevice = device; + mCallback.notify = &JTvInputHal::notify; + + mDevice->initialize(mDevice, &mCallback, this); +} + +JTvInputHal::~JTvInputHal() { + mDevice->common.close((hw_device_t*)mDevice); + + JNIEnv* env = AndroidRuntime::getJNIEnv(); + env->DeleteWeakGlobalRef(mThiz); + mThiz = NULL; +} + +JTvInputHal* JTvInputHal::createInstance(JNIEnv* env, jobject thiz) { + tv_input_module_t* module = NULL; + status_t err = hw_get_module(TV_INPUT_HARDWARE_MODULE_ID, + (hw_module_t const**)&module); + if (err) { + ALOGE("Couldn't load %s module (%s)", + TV_INPUT_HARDWARE_MODULE_ID, strerror(-err)); + return 0; + } + + tv_input_device_t* device = NULL; + err = module->common.methods->open( + (hw_module_t*)module, + TV_INPUT_DEFAULT_DEVICE, + (hw_device_t**)&device); + if (err) { + ALOGE("Couldn't open %s device (%s)", + TV_INPUT_DEFAULT_DEVICE, strerror(-err)); + return 0; + } + + return new JTvInputHal(env, thiz, device); +} + +int JTvInputHal::setSurface(int deviceId, int streamId, const sp<Surface>& surface) { + Connection& connection = mConnections.editValueFor(deviceId); + if (connection.mStreamId == streamId && connection.mSurface == surface) { + // Nothing to do + return NO_ERROR; + } + if (Surface::isValid(connection.mSurface)) { + connection.mSurface.clear(); + } + if (surface == NULL) { + if (connection.mSurface != NULL) { + connection.mSurface->setSidebandStream(NULL); + connection.mSurface.clear(); + } + if (connection.mSourceHandle != NULL) { + // Need to reset streams + if (mDevice->close_stream( + mDevice, deviceId, connection.mStreamId) != 0) { + ALOGE("Couldn't remove stream"); + return BAD_VALUE; + } + connection.mSourceHandle.clear(); + } + return NO_ERROR; + } + connection.mSurface = surface; + if (connection.mSourceHandle == NULL) { + // Need to configure stream + int numConfigs = 0; + const tv_stream_config_t* configs = NULL; + if (mDevice->get_stream_configurations( + mDevice, deviceId, &numConfigs, &configs) != 0) { + ALOGE("Couldn't get stream configs"); + return UNKNOWN_ERROR; + } + int configIndex = -1; + for (int i = 0; i < numConfigs; ++i) { + if (configs[i].stream_id == streamId) { + configIndex = i; + break; + } + } + if (configIndex == -1) { + ALOGE("Cannot find a config with given stream ID: %d", streamId); + return BAD_VALUE; + } + // TODO: handle buffer producer profile. + if (configs[configIndex].type != + TV_STREAM_TYPE_INDEPENDENT_VIDEO_SOURCE) { + ALOGE("Profiles other than independent video source is not yet " + "supported : type = %d", configs[configIndex].type); + return INVALID_OPERATION; + } + tv_stream_t stream; + stream.stream_id = configs[configIndex].stream_id; + if (mDevice->open_stream(mDevice, deviceId, &stream) != 0) { + ALOGE("Couldn't add stream"); + return UNKNOWN_ERROR; + } + connection.mSourceHandle = NativeHandle::create( + stream.sideband_stream_source_handle, false); + connection.mStreamId = stream.stream_id; + connection.mSurface->setSidebandStream(connection.mSourceHandle); + } + return NO_ERROR; +} + +const tv_stream_config_t* JTvInputHal::getStreamConfigs(int deviceId, int* numConfigs) { + const tv_stream_config_t* configs = NULL; + if (mDevice->get_stream_configurations( + mDevice, deviceId, numConfigs, &configs) != 0) { + ALOGE("Couldn't get stream configs"); + return NULL; + } + return configs; +} + + +// static +void JTvInputHal::notify( + tv_input_device_t* dev, tv_input_event_t* event, void* data) { + JTvInputHal* thiz = (JTvInputHal*)data; + switch (event->type) { + case TV_INPUT_EVENT_DEVICE_AVAILABLE: { + thiz->onDeviceAvailable(event->device_info); + } break; + case TV_INPUT_EVENT_DEVICE_UNAVAILABLE: { + thiz->onDeviceUnavailable(event->device_info.device_id); + } break; + case TV_INPUT_EVENT_STREAM_CONFIGURATIONS_CHANGED: { + thiz->onStreamConfigurationsChanged(event->device_info.device_id); + } break; + default: + ALOGE("Unrecognizable event"); + } +} + +void JTvInputHal::onDeviceAvailable(const tv_input_device_info_t& info) { + JNIEnv* env = AndroidRuntime::getJNIEnv(); + mConnections.add(info.device_id, Connection()); + env->CallVoidMethod( + mThiz, + gTvInputHalClassInfo.deviceAvailable, + info.device_id, + info.type); +} + +void JTvInputHal::onDeviceUnavailable(int deviceId) { + JNIEnv* env = AndroidRuntime::getJNIEnv(); + mConnections.removeItem(deviceId); + env->CallVoidMethod( + mThiz, + gTvInputHalClassInfo.deviceUnavailable, + deviceId); +} + +void JTvInputHal::onStreamConfigurationsChanged(int deviceId) { + JNIEnv* env = AndroidRuntime::getJNIEnv(); + mConnections.removeItem(deviceId); + env->CallVoidMethod( + mThiz, + gTvInputHalClassInfo.streamConfigsChanged, + deviceId); +} + +//////////////////////////////////////////////////////////////////////////////// + +static jlong nativeOpen(JNIEnv* env, jobject thiz) { + return (jlong)JTvInputHal::createInstance(env, thiz); +} + +static int nativeSetSurface(JNIEnv* env, jclass clazz, + jlong ptr, jint deviceId, jint streamId, jobject jsurface) { + JTvInputHal* tvInputHal = (JTvInputHal*)ptr; + sp<Surface> surface( + jsurface + ? android_view_Surface_getSurface(env, jsurface) + : NULL); + return tvInputHal->setSurface(deviceId, streamId, surface); +} + +static jobjectArray nativeGetStreamConfigs(JNIEnv* env, jclass clazz, + jlong ptr, jint deviceId, jint generation) { + JTvInputHal* tvInputHal = (JTvInputHal*)ptr; + int numConfigs = 0; + const tv_stream_config_t* configs = tvInputHal->getStreamConfigs(deviceId, &numConfigs); + + jobjectArray result = env->NewObjectArray(numConfigs, gTvStreamConfigClassInfo.clazz, NULL); + for (int i = 0; i < numConfigs; ++i) { + jobject builder = env->NewObject( + gTvStreamConfigBuilderClassInfo.clazz, + gTvStreamConfigBuilderClassInfo.constructor); + env->CallObjectMethod( + builder, gTvStreamConfigBuilderClassInfo.streamId, configs[i].stream_id); + env->CallObjectMethod( + builder, gTvStreamConfigBuilderClassInfo.type, configs[i].type); + env->CallObjectMethod( + builder, gTvStreamConfigBuilderClassInfo.maxWidth, configs[i].max_video_width); + env->CallObjectMethod( + builder, gTvStreamConfigBuilderClassInfo.maxHeight, configs[i].max_video_height); + env->CallObjectMethod( + builder, gTvStreamConfigBuilderClassInfo.generation, generation); + + jobject config = env->CallObjectMethod(builder, gTvStreamConfigBuilderClassInfo.build); + + env->SetObjectArrayElement(result, i, config); + + env->DeleteLocalRef(config); + env->DeleteLocalRef(builder); + } + return result; +} + +static void nativeClose(JNIEnv* env, jclass clazz, jlong ptr) { + JTvInputHal* tvInputHal = (JTvInputHal*)ptr; + delete tvInputHal; +} + +static JNINativeMethod gTvInputHalMethods[] = { + /* name, signature, funcPtr */ + { "nativeOpen", "()J", + (void*) nativeOpen }, + { "nativeSetSurface", "(JIILandroid/view/Surface;)I", + (void*) nativeSetSurface }, + { "nativeGetStreamConfigs", "(JII)[Landroid/tv/TvStreamConfig;", + (void*) nativeGetStreamConfigs }, + { "nativeClose", "(J)V", + (void*) nativeClose }, +}; + +#define FIND_CLASS(var, className) \ + var = env->FindClass(className); \ + LOG_FATAL_IF(! var, "Unable to find class " className) + +#define GET_METHOD_ID(var, clazz, methodName, fieldDescriptor) \ + var = env->GetMethodID(clazz, methodName, fieldDescriptor); \ + LOG_FATAL_IF(! var, "Unable to find method" methodName) + +int register_android_server_tv_TvInputHal(JNIEnv* env) { + int res = jniRegisterNativeMethods(env, "com/android/server/tv/TvInputHal", + gTvInputHalMethods, NELEM(gTvInputHalMethods)); + LOG_FATAL_IF(res < 0, "Unable to register native methods."); + + jclass clazz; + FIND_CLASS(clazz, "com/android/server/tv/TvInputHal"); + + GET_METHOD_ID( + gTvInputHalClassInfo.deviceAvailable, clazz, "deviceAvailableFromNative", "(II)V"); + GET_METHOD_ID( + gTvInputHalClassInfo.deviceUnavailable, clazz, "deviceUnavailableFromNative", "(I)V"); + GET_METHOD_ID( + gTvInputHalClassInfo.streamConfigsChanged, clazz, + "streamConfigsChangedFromNative", "(I)V"); + + FIND_CLASS(gTvStreamConfigClassInfo.clazz, "android/tv/TvStreamConfig"); + gTvStreamConfigClassInfo.clazz = jclass(env->NewGlobalRef(gTvStreamConfigClassInfo.clazz)); + + FIND_CLASS(gTvStreamConfigBuilderClassInfo.clazz, "android/tv/TvStreamConfig$Builder"); + gTvStreamConfigBuilderClassInfo.clazz = + jclass(env->NewGlobalRef(gTvStreamConfigBuilderClassInfo.clazz)); + + GET_METHOD_ID( + gTvStreamConfigBuilderClassInfo.constructor, + gTvStreamConfigBuilderClassInfo.clazz, + "<init>", "()V"); + GET_METHOD_ID( + gTvStreamConfigBuilderClassInfo.streamId, + gTvStreamConfigBuilderClassInfo.clazz, + "streamId", "(I)Landroid/tv/TvStreamConfig$Builder;"); + GET_METHOD_ID( + gTvStreamConfigBuilderClassInfo.type, + gTvStreamConfigBuilderClassInfo.clazz, + "type", "(I)Landroid/tv/TvStreamConfig$Builder;"); + GET_METHOD_ID( + gTvStreamConfigBuilderClassInfo.maxWidth, + gTvStreamConfigBuilderClassInfo.clazz, + "maxWidth", "(I)Landroid/tv/TvStreamConfig$Builder;"); + GET_METHOD_ID( + gTvStreamConfigBuilderClassInfo.maxHeight, + gTvStreamConfigBuilderClassInfo.clazz, + "maxHeight", "(I)Landroid/tv/TvStreamConfig$Builder;"); + GET_METHOD_ID( + gTvStreamConfigBuilderClassInfo.generation, + gTvStreamConfigBuilderClassInfo.clazz, + "generation", "(I)Landroid/tv/TvStreamConfig$Builder;"); + GET_METHOD_ID( + gTvStreamConfigBuilderClassInfo.build, + gTvStreamConfigBuilderClassInfo.clazz, + "build", "()Landroid/tv/TvStreamConfig;"); + + return 0; +} + +} /* namespace android */ diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp index 1feb325..bfa8286 100644 --- a/services/core/jni/onload.cpp +++ b/services/core/jni/onload.cpp @@ -41,6 +41,7 @@ int register_android_server_dreams_McuHal(JNIEnv* env); int register_android_server_hdmi_HdmiCecController(JNIEnv* env); int register_android_server_hdmi_HdmiCecService(JNIEnv* env); int register_android_server_hdmi_HdmiMhlController(JNIEnv* env); +int register_android_server_tv_TvInputHal(JNIEnv* env); }; using namespace android; @@ -78,6 +79,7 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* reserved) // TODO: remove this once replaces HdmiCecService with HdmiControlService. register_android_server_hdmi_HdmiCecService(env); register_android_server_hdmi_HdmiMhlController(env); + register_android_server_tv_TvInputHal(env); return JNI_VERSION_1_4; } diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index de46b16..0f24ff6 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -83,6 +83,7 @@ import com.android.server.power.ShutdownThread; import com.android.server.search.SearchManagerService; import com.android.server.statusbar.StatusBarManagerService; import com.android.server.storage.DeviceStorageMonitorService; +import com.android.server.task.TaskManagerService; import com.android.server.trust.TrustManagerService; import com.android.server.tv.TvInputManagerService; import com.android.server.twilight.TwilightService; @@ -132,6 +133,8 @@ public final class SystemServer { "com.android.server.hdmi.HdmiCecService"; private static final String ETHERNET_SERVICE_CLASS = "com.android.server.ethernet.EthernetService"; + private static final String TASK_SERVICE_CLASS = + "com.android.server.task.TaskManagerService"; private final int mFactoryTestMode; private Timer mProfilerSnapshotTimer; @@ -183,7 +186,7 @@ public final class SystemServer { // had to fallback to a different runtime because it is // running as root and we need to be the system user to set // the property. http://b/11463182 - SystemProperties.set("persist.sys.dalvik.vm.lib.1", VMRuntime.getRuntime().vmLibrary()); + SystemProperties.set("persist.sys.dalvik.vm.lib.2", VMRuntime.getRuntime().vmLibrary()); // Enable the sampling profiler. if (SamplingProfilerIntegration.isEnabled()) { @@ -831,6 +834,8 @@ public final class SystemServer { mSystemServiceManager.startService(UiModeManagerService.class); + mSystemServiceManager.startService(TaskManagerService.class); + if (!disableNonCoreServices) { try { if (pm.hasSystemFeature(PackageManager.FEATURE_BACKUP)) { diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java index 9b6daad..62ff121 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java @@ -113,7 +113,7 @@ class VoiceInteractionManagerServiceImpl { if (mBound) { try { mIWindowManager.addWindowToken(mToken, - WindowManager.LayoutParams.TYPE_INPUT_METHOD); + WindowManager.LayoutParams.TYPE_VOICE_INTERACTION); } catch (RemoteException e) { Slog.w(TAG, "Failed adding window token", e); } |