1851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate/* 2851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate * Copyright (C) 2014 The Android Open Source Project 3851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate * 4851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate * Licensed under the Apache License, Version 2.0 (the "License"); 5851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate * you may not use this file except in compliance with the License. 6851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate * You may obtain a copy of the License at 7851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate * 8851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate * http://www.apache.org/licenses/LICENSE-2.0 9851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate * 10851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate * Unless required by applicable law or agreed to in writing, software 11851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate * distributed under the License is distributed on an "AS IS" BASIS, 12851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate * See the License for the specific language governing permissions and 14851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate * limitations under the License 15851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate */ 16851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate 177060b04f6d92351b67222e636ab378a0273bf3e7Christopher Tatepackage com.android.server.job.controllers; 18851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate 19effacfa75bd9c2ebc889a7bc4f002c07f82f4c31Matthew Williamsimport java.io.PrintWriter; 20851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate 21851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tateimport android.app.AlarmManager; 22851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tateimport android.app.PendingIntent; 23851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tateimport android.content.BroadcastReceiver; 24851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tateimport android.content.Context; 25851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tateimport android.content.Intent; 26851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tateimport android.content.IntentFilter; 27851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tateimport android.os.SystemClock; 28e9a988caca733d2f292991a52a0047685a69812fDianne Hackbornimport android.os.UserHandle; 29f9bac16d61db0fceb15484587ecf876c2b802c37Dianne Hackbornimport android.util.ArraySet; 30851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tateimport android.util.Slog; 31851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate 3227d92e4e397728d56f4f951dd4ce99668c7c447bChristopher Tateimport com.android.server.am.ActivityManagerService; 337060b04f6d92351b67222e636ab378a0273bf3e7Christopher Tateimport com.android.server.job.JobSchedulerService; 347060b04f6d92351b67222e636ab378a0273bf3e7Christopher Tateimport com.android.server.job.StateChangedListener; 35851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate 366466c1cc5efc4ff05fabdd670cf78a6a7ca45afbDianne Hackbornpublic final class IdleController extends StateController { 37851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate private static final String TAG = "IdleController"; 38851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate 39ca5edbb6b5300e07a5f7bfbec36f08df65d8f6e7Yao Chen // Policy: we decide that we're "idle" if the device has been unused / 40ca5edbb6b5300e07a5f7bfbec36f08df65d8f6e7Yao Chen // screen off or dreaming for at least this long 41ca5edbb6b5300e07a5f7bfbec36f08df65d8f6e7Yao Chen private long mInactivityIdleThreshold; 42ca5edbb6b5300e07a5f7bfbec36f08df65d8f6e7Yao Chen private long mIdleWindowSlop; 43f9bac16d61db0fceb15484587ecf876c2b802c37Dianne Hackborn final ArraySet<JobStatus> mTrackedTasks = new ArraySet<>(); 44851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate IdlenessTracker mIdleTracker; 45851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate 46851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate // Singleton factory 47851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate private static Object sCreationLock = new Object(); 48851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate private static volatile IdleController sController; 49851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate 507060b04f6d92351b67222e636ab378a0273bf3e7Christopher Tate public static IdleController get(JobSchedulerService service) { 51851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate synchronized (sCreationLock) { 52851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate if (sController == null) { 5333d31c5b70c7d056e799e34bb6eccbe6939714eaDianne Hackborn sController = new IdleController(service, service.getContext(), service.getLock()); 54851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate } 55851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate return sController; 56851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate } 57851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate } 58851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate 5933d31c5b70c7d056e799e34bb6eccbe6939714eaDianne Hackborn private IdleController(StateChangedListener stateChangedListener, Context context, 6033d31c5b70c7d056e799e34bb6eccbe6939714eaDianne Hackborn Object lock) { 6133d31c5b70c7d056e799e34bb6eccbe6939714eaDianne Hackborn super(stateChangedListener, context, lock); 62851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate initIdleStateTracking(); 63851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate } 64851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate 65851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate /** 66851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate * StateController interface 67851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate */ 68851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate @Override 69b0001f6fb1383d9824c2733896b0b348e7f77240Dianne Hackborn public void maybeStartTrackingJobLocked(JobStatus taskStatus, JobStatus lastJob) { 70851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate if (taskStatus.hasIdleConstraint()) { 71b0001f6fb1383d9824c2733896b0b348e7f77240Dianne Hackborn mTrackedTasks.add(taskStatus); 72f9bac16d61db0fceb15484587ecf876c2b802c37Dianne Hackborn taskStatus.setTrackingController(JobStatus.TRACKING_IDLE); 73b0001f6fb1383d9824c2733896b0b348e7f77240Dianne Hackborn taskStatus.setIdleConstraintSatisfied(mIdleTracker.isIdle()); 74851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate } 75851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate } 76851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate 77851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate @Override 78f9bac16d61db0fceb15484587ecf876c2b802c37Dianne Hackborn public void maybeStopTrackingJobLocked(JobStatus taskStatus, JobStatus incomingJob, 79f9bac16d61db0fceb15484587ecf876c2b802c37Dianne Hackborn boolean forUpdate) { 80f9bac16d61db0fceb15484587ecf876c2b802c37Dianne Hackborn if (taskStatus.clearTrackingController(JobStatus.TRACKING_IDLE)) { 81f9bac16d61db0fceb15484587ecf876c2b802c37Dianne Hackborn mTrackedTasks.remove(taskStatus); 82f9bac16d61db0fceb15484587ecf876c2b802c37Dianne Hackborn } 83851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate } 84851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate 85851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate /** 86851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate * Interaction with the task manager service 87851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate */ 88851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate void reportNewIdleState(boolean isIdle) { 8933d31c5b70c7d056e799e34bb6eccbe6939714eaDianne Hackborn synchronized (mLock) { 90f9bac16d61db0fceb15484587ecf876c2b802c37Dianne Hackborn for (int i = mTrackedTasks.size()-1; i >= 0; i--) { 91f9bac16d61db0fceb15484587ecf876c2b802c37Dianne Hackborn mTrackedTasks.valueAt(i).setIdleConstraintSatisfied(isIdle); 92851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate } 93851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate } 949b9244b6941110ea2d940d9fc8eed0cdff96a016Matthew Williams mStateChangedListener.onControllerStateChanged(); 95851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate } 96851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate 97851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate /** 98851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate * Idle state tracking, and messaging with the task manager when 99851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate * significant state changes occur 100851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate */ 101851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate private void initIdleStateTracking() { 102ca5edbb6b5300e07a5f7bfbec36f08df65d8f6e7Yao Chen mInactivityIdleThreshold = mContext.getResources().getInteger( 103ca5edbb6b5300e07a5f7bfbec36f08df65d8f6e7Yao Chen com.android.internal.R.integer.config_jobSchedulerInactivityIdleThreshold); 104ca5edbb6b5300e07a5f7bfbec36f08df65d8f6e7Yao Chen mIdleWindowSlop = mContext.getResources().getInteger( 105ca5edbb6b5300e07a5f7bfbec36f08df65d8f6e7Yao Chen com.android.internal.R.integer.config_jobSchedulerIdleWindowSlop); 106851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate mIdleTracker = new IdlenessTracker(); 107851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate mIdleTracker.startTracking(); 108851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate } 109851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate 1106466c1cc5efc4ff05fabdd670cf78a6a7ca45afbDianne Hackborn final class IdlenessTracker extends BroadcastReceiver { 111851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate private AlarmManager mAlarm; 112851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate private PendingIntent mIdleTriggerIntent; 113851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate boolean mIdle; 114ccfe87356fb961a4e85e4fce7cc5e0fac410f131Kevin Zhu boolean mScreenOn; 115851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate 116851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate public IdlenessTracker() { 117851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate mAlarm = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE); 118851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate 11927d92e4e397728d56f4f951dd4ce99668c7c447bChristopher Tate Intent intent = new Intent(ActivityManagerService.ACTION_TRIGGER_IDLE) 120be0c4175398ff5d7e13209e833b3037cdd0207d7Matthew Williams .setPackage("android") 121be0c4175398ff5d7e13209e833b3037cdd0207d7Matthew Williams .setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); 122851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate mIdleTriggerIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0); 123851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate 124be0c4175398ff5d7e13209e833b3037cdd0207d7Matthew Williams // At boot we presume that the user has just "interacted" with the 125be0c4175398ff5d7e13209e833b3037cdd0207d7Matthew Williams // device in some meaningful way. 126851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate mIdle = false; 127ccfe87356fb961a4e85e4fce7cc5e0fac410f131Kevin Zhu mScreenOn = true; 128851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate } 129851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate 130851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate public boolean isIdle() { 131851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate return mIdle; 132851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate } 133851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate 134851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate public void startTracking() { 135851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate IntentFilter filter = new IntentFilter(); 136851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate 137851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate // Screen state 138851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate filter.addAction(Intent.ACTION_SCREEN_ON); 139851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate filter.addAction(Intent.ACTION_SCREEN_OFF); 140851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate 141851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate // Dreaming state 142851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate filter.addAction(Intent.ACTION_DREAMING_STARTED); 143851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate filter.addAction(Intent.ACTION_DREAMING_STOPPED); 144851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate 145b9583c9d93bd1d2c9d506dffae87a5ca2b7f7307Christopher Tate // Debugging/instrumentation 14627d92e4e397728d56f4f951dd4ce99668c7c447bChristopher Tate filter.addAction(ActivityManagerService.ACTION_TRIGGER_IDLE); 147b9583c9d93bd1d2c9d506dffae87a5ca2b7f7307Christopher Tate 148851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate mContext.registerReceiver(this, filter); 149851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate } 150851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate 151851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate @Override 152851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate public void onReceive(Context context, Intent intent) { 153851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate final String action = intent.getAction(); 154851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate if (action.equals(Intent.ACTION_SCREEN_ON) 155851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate || action.equals(Intent.ACTION_DREAMING_STOPPED)) { 156ccfe87356fb961a4e85e4fce7cc5e0fac410f131Kevin Zhu if (DEBUG) { 157ccfe87356fb961a4e85e4fce7cc5e0fac410f131Kevin Zhu Slog.v(TAG,"exiting idle : " + action); 158ccfe87356fb961a4e85e4fce7cc5e0fac410f131Kevin Zhu } 159ccfe87356fb961a4e85e4fce7cc5e0fac410f131Kevin Zhu mScreenOn = true; 160ccfe87356fb961a4e85e4fce7cc5e0fac410f131Kevin Zhu //cancel the alarm 161ccfe87356fb961a4e85e4fce7cc5e0fac410f131Kevin Zhu mAlarm.cancel(mIdleTriggerIntent); 162851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate if (mIdle) { 163ccfe87356fb961a4e85e4fce7cc5e0fac410f131Kevin Zhu // possible transition to not-idle 164851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate mIdle = false; 165851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate reportNewIdleState(mIdle); 166851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate } 167851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate } else if (action.equals(Intent.ACTION_SCREEN_OFF) 168851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate || action.equals(Intent.ACTION_DREAMING_STARTED)) { 169851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate // when the screen goes off or dreaming starts, we schedule the 170851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate // alarm that will tell us when we have decided the device is 171851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate // truly idle. 172be0c4175398ff5d7e13209e833b3037cdd0207d7Matthew Williams final long nowElapsed = SystemClock.elapsedRealtime(); 173ca5edbb6b5300e07a5f7bfbec36f08df65d8f6e7Yao Chen final long when = nowElapsed + mInactivityIdleThreshold; 174851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate if (DEBUG) { 175be0c4175398ff5d7e13209e833b3037cdd0207d7Matthew Williams Slog.v(TAG, "Scheduling idle : " + action + " now:" + nowElapsed + " when=" 176be0c4175398ff5d7e13209e833b3037cdd0207d7Matthew Williams + when); 177851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate } 178ccfe87356fb961a4e85e4fce7cc5e0fac410f131Kevin Zhu mScreenOn = false; 179851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate mAlarm.setWindow(AlarmManager.ELAPSED_REALTIME_WAKEUP, 180ca5edbb6b5300e07a5f7bfbec36f08df65d8f6e7Yao Chen when, mIdleWindowSlop, mIdleTriggerIntent); 18127d92e4e397728d56f4f951dd4ce99668c7c447bChristopher Tate } else if (action.equals(ActivityManagerService.ACTION_TRIGGER_IDLE)) { 182ccfe87356fb961a4e85e4fce7cc5e0fac410f131Kevin Zhu // idle time starts now. Do not set mIdle if screen is on. 183ccfe87356fb961a4e85e4fce7cc5e0fac410f131Kevin Zhu if (!mIdle && !mScreenOn) { 184851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate if (DEBUG) { 185851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate Slog.v(TAG, "Idle trigger fired @ " + SystemClock.elapsedRealtime()); 186851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate } 187851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate mIdle = true; 188851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate reportNewIdleState(mIdle); 1897234fc6fef781e33842bc598a6e9e04bbff4f898Christopher Tate } else { 1907234fc6fef781e33842bc598a6e9e04bbff4f898Christopher Tate if (DEBUG) { 1917234fc6fef781e33842bc598a6e9e04bbff4f898Christopher Tate Slog.v(TAG, "TRIGGER_IDLE received but not changing state; idle=" 1927234fc6fef781e33842bc598a6e9e04bbff4f898Christopher Tate + mIdle + " screen=" + mScreenOn); 1937234fc6fef781e33842bc598a6e9e04bbff4f898Christopher Tate } 194851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate } 195851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate } 196851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate } 197851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate } 198effacfa75bd9c2ebc889a7bc4f002c07f82f4c31Matthew Williams 199effacfa75bd9c2ebc889a7bc4f002c07f82f4c31Matthew Williams @Override 200ef3aa6ee53c5e4f1c50dd5a9b5821c54e449d4b3Dianne Hackborn public void dumpControllerStateLocked(PrintWriter pw, int filterUid) { 201b0001f6fb1383d9824c2733896b0b348e7f77240Dianne Hackborn pw.print("Idle: "); 2027234fc6fef781e33842bc598a6e9e04bbff4f898Christopher Tate pw.println(mIdleTracker.isIdle()); 203e9a988caca733d2f292991a52a0047685a69812fDianne Hackborn pw.print("Tracking "); 204e9a988caca733d2f292991a52a0047685a69812fDianne Hackborn pw.print(mTrackedTasks.size()); 205e9a988caca733d2f292991a52a0047685a69812fDianne Hackborn pw.println(":"); 206b0001f6fb1383d9824c2733896b0b348e7f77240Dianne Hackborn for (int i = 0; i < mTrackedTasks.size(); i++) { 207f9bac16d61db0fceb15484587ecf876c2b802c37Dianne Hackborn final JobStatus js = mTrackedTasks.valueAt(i); 208ef3aa6ee53c5e4f1c50dd5a9b5821c54e449d4b3Dianne Hackborn if (!js.shouldDump(filterUid)) { 209ef3aa6ee53c5e4f1c50dd5a9b5821c54e449d4b3Dianne Hackborn continue; 210ef3aa6ee53c5e4f1c50dd5a9b5821c54e449d4b3Dianne Hackborn } 211e9a988caca733d2f292991a52a0047685a69812fDianne Hackborn pw.print(" #"); 212e9a988caca733d2f292991a52a0047685a69812fDianne Hackborn js.printUniqueId(pw); 213e9a988caca733d2f292991a52a0047685a69812fDianne Hackborn pw.print(" from "); 214e9a988caca733d2f292991a52a0047685a69812fDianne Hackborn UserHandle.formatUid(pw, js.getSourceUid()); 215e9a988caca733d2f292991a52a0047685a69812fDianne Hackborn pw.println(); 2165eeb59cceb1f95813c548c1c5937f161c1ed3571Christopher Tate } 217effacfa75bd9c2ebc889a7bc4f002c07f82f4c31Matthew Williams } 218851f3d5110c48bcdba4762739c4ad9b9b479d9afChristopher Tate} 219