1 /* 2 * Copyright (C) 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License 15 */ 16 17 package android.server.wm.lifecycle; 18 19 import static android.server.wm.StateLogger.log; 20 21 import android.app.Activity; 22 import android.content.ContentProvider; 23 import android.content.ContentProviderClient; 24 import android.content.ContentValues; 25 import android.database.Cursor; 26 import android.net.Uri; 27 import android.os.Bundle; 28 import android.os.RemoteException; 29 import android.util.Pair; 30 31 import java.util.ArrayList; 32 import java.util.List; 33 34 /** 35 * Used as a shared log storage of activity lifecycle transitions. Methods must be synchronized to 36 * prevent concurrent modification of the log store. 37 */ 38 public class LifecycleLog extends ContentProvider { 39 40 public enum ActivityCallback { 41 PRE_ON_CREATE, 42 ON_CREATE, 43 ON_START, 44 ON_RESUME, 45 ON_PAUSE, 46 ON_STOP, 47 ON_RESTART, 48 ON_DESTROY, 49 ON_ACTIVITY_RESULT, 50 ON_POST_CREATE, 51 ON_NEW_INTENT, 52 ON_MULTI_WINDOW_MODE_CHANGED, 53 ON_TOP_POSITION_GAINED, 54 ON_TOP_POSITION_LOST 55 } 56 57 interface LifecycleTrackerCallback { 58 void onActivityLifecycleChanged(); 59 } 60 61 /** Identifies the activity to which the event corresponds. */ 62 private static final String EXTRA_KEY_ACTIVITY = "key_activity"; 63 /** Puts a lifecycle or callback into the container. */ 64 private static final String METHOD_ADD_CALLBACK = "add_callback"; 65 /** Content provider URI for cross-process lifecycle transitions collecting. */ 66 private static final Uri URI = Uri.parse("content://android.server.wm.lifecycle.logprovider"); 67 68 /** 69 * Log for encountered activity callbacks. Note that methods accessing or modifying this 70 * list should be synchronized as it can be accessed from different threads. 71 */ 72 private final static List<Pair<String, ActivityCallback>> sLog = new ArrayList<>(); 73 74 /** 75 * Lifecycle tracker interface that waits for correct states or sequences. 76 */ 77 private static LifecycleTrackerCallback sLifecycleTracker; 78 79 /** Clear the entire transition log. */ 80 void clear() { 81 synchronized(sLog) { 82 sLog.clear(); 83 } 84 } 85 86 public void setLifecycleTracker(LifecycleTrackerCallback lifecycleTracker) { 87 sLifecycleTracker = lifecycleTracker; 88 } 89 90 /** Add activity callback to the log. */ 91 private void onActivityCallback(String activityCanonicalName, 92 ActivityCallback callback) { 93 synchronized (sLog) { 94 sLog.add(new Pair<>(activityCanonicalName, callback)); 95 } 96 log("Activity " + activityCanonicalName + " receiver callback " + callback); 97 // Trigger check for valid state in the tracker 98 if (sLifecycleTracker != null) { 99 sLifecycleTracker.onActivityLifecycleChanged(); 100 } 101 } 102 103 /** Get logs for all recorded transitions. */ 104 List<Pair<String, ActivityCallback>> getLog() { 105 // Wrap in a new list to prevent concurrent modification 106 synchronized(sLog) { 107 return new ArrayList<>(sLog); 108 } 109 } 110 111 /** Get transition logs for the specified activity. */ 112 List<ActivityCallback> getActivityLog(Class<? extends Activity> activityClass) { 113 final String activityName = activityClass.getCanonicalName(); 114 log("Looking up log for activity: " + activityName); 115 final List<ActivityCallback> activityLog = new ArrayList<>(); 116 synchronized(sLog) { 117 for (Pair<String, ActivityCallback> transition : sLog) { 118 if (transition.first.equals(activityName)) { 119 activityLog.add(transition.second); 120 } 121 } 122 } 123 return activityLog; 124 } 125 126 127 // ContentProvider implementation for cross-process tracking 128 129 public static class LifecycleLogClient implements AutoCloseable { 130 private static final String EMPTY_ARG = ""; 131 private final ContentProviderClient mClient; 132 private final String mOwner; 133 134 LifecycleLogClient(ContentProviderClient client, Activity owner) { 135 mClient = client; 136 mOwner = owner.getClass().getCanonicalName(); 137 } 138 139 void onActivityCallback(ActivityCallback callback) { 140 final Bundle extras = new Bundle(); 141 extras.putInt(METHOD_ADD_CALLBACK, callback.ordinal()); 142 extras.putString(EXTRA_KEY_ACTIVITY, mOwner); 143 try { 144 mClient.call(METHOD_ADD_CALLBACK, EMPTY_ARG, extras); 145 } catch (RemoteException e) { 146 throw new RuntimeException(e); 147 } 148 } 149 150 @Override 151 public void close() { 152 mClient.close(); 153 } 154 155 static LifecycleLogClient create(Activity owner) { 156 final ContentProviderClient client = owner.getContentResolver() 157 .acquireContentProviderClient(URI); 158 if (client == null) { 159 throw new RuntimeException("Unable to acquire " + URI); 160 } 161 return new LifecycleLogClient(client, owner); 162 } 163 } 164 165 @Override 166 public Bundle call(String method, String arg, Bundle extras) { 167 if (!METHOD_ADD_CALLBACK.equals(method)) { 168 throw new UnsupportedOperationException(); 169 } 170 onActivityCallback(extras.getString(EXTRA_KEY_ACTIVITY), 171 ActivityCallback.values()[extras.getInt(method)]); 172 return null; 173 } 174 175 @Override 176 public boolean onCreate() { 177 return true; 178 } 179 180 @Override 181 public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, 182 String sortOrder) { 183 return null; 184 } 185 186 @Override 187 public String getType(Uri uri) { 188 return null; 189 } 190 191 @Override 192 public Uri insert(Uri uri, ContentValues values) { 193 return null; 194 } 195 196 @Override 197 public int delete(Uri uri, String selection, String[] selectionArgs) { 198 return 0; 199 } 200 201 @Override 202 public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { 203 return 0; 204 } 205 } 206