1 /* 2 * Copyright (C) 2022 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.activity.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.content.Context; 26 import android.database.Cursor; 27 import android.net.Uri; 28 import android.os.Bundle; 29 import android.os.RemoteException; 30 import android.util.Pair; 31 32 import java.util.ArrayList; 33 import java.util.List; 34 35 /** 36 * Used as a shared log storage of events such as activity lifecycle. Methods must be 37 * synchronized to prevent concurrent modification of the log store. 38 */ 39 public class EventLog extends ContentProvider { 40 41 interface EventTrackerCallback { onEventObserved()42 void onEventObserved(); 43 } 44 45 /** Identifies the activity to which the event corresponds. */ 46 private static final String EXTRA_KEY_TAG = "key_activity"; 47 /** Puts a lifecycle or callback into the container. */ 48 private static final String METHOD_ADD_CALLBACK = "add_callback"; 49 50 /** 51 * Log for encountered activity callbacks. Note that methods accessing or modifying this 52 * list should be synchronized as it can be accessed from different threads. 53 */ 54 private static final List<Pair<String, String>> sLog = new ArrayList<>(); 55 56 /** 57 * Event tracker interface that waits for correct states or sequences. 58 */ 59 private static EventTrackerCallback sEventTracker; 60 61 /** Clear the entire transition log. */ clear()62 public void clear() { 63 synchronized (sLog) { 64 sLog.clear(); 65 } 66 } 67 setEventTracker(EventTrackerCallback eventTracker)68 public void setEventTracker(EventTrackerCallback eventTracker) { 69 synchronized (sLog) { 70 sEventTracker = eventTracker; 71 } 72 } 73 74 /** Add activity callback to the log. */ onActivityCallback(String activityCanonicalName, String callback)75 private void onActivityCallback(String activityCanonicalName, String callback) { 76 synchronized (sLog) { 77 sLog.add(new Pair<>(activityCanonicalName, callback)); 78 } 79 log("Activity " + activityCanonicalName + " receiver callback " + callback); 80 // Trigger check for valid state in the tracker 81 if (sEventTracker != null) { 82 sEventTracker.onEventObserved(); 83 } 84 } 85 86 /** Get logs for all recorded transitions. */ getLog()87 public List<Pair<String, String>> getLog() { 88 // Wrap in a new list to prevent concurrent modification 89 synchronized (sLog) { 90 return new ArrayList<>(sLog); 91 } 92 } 93 94 /** Get transition logs for the specified activity. */ getActivityLog(Class<? extends Activity> activityClass)95 List<String> getActivityLog(Class<? extends Activity> activityClass) { 96 final String activityName = activityClass.getCanonicalName(); 97 log("Looking up log for activity: " + activityName); 98 final List<String> activityLog = new ArrayList<>(); 99 synchronized (sLog) { 100 for (Pair<String, String> transition : sLog) { 101 if (transition.first.equals(activityName)) { 102 activityLog.add(transition.second); 103 } 104 } 105 } 106 return activityLog; 107 } 108 109 110 // ContentProvider implementation for cross-process tracking 111 112 public static class EventLogClient implements AutoCloseable { 113 private static final String EMPTY_ARG = ""; 114 private final ContentProviderClient mClient; 115 private final String mTag; 116 EventLogClient(ContentProviderClient client, String tag)117 public EventLogClient(ContentProviderClient client, String tag) { 118 mClient = client; 119 mTag = tag; 120 } 121 onCallback(String callback)122 public void onCallback(String callback) { 123 onCallback(callback, mTag); 124 } 125 onCallback(String callback, Activity activity)126 public void onCallback(String callback, Activity activity) { 127 onCallback(callback, activity.getClass().getCanonicalName()); 128 } 129 onCallback(String callback, String tag)130 public void onCallback(String callback, String tag) { 131 final Bundle extras = new Bundle(); 132 extras.putString(METHOD_ADD_CALLBACK, callback); 133 extras.putString(EXTRA_KEY_TAG, tag); 134 try { 135 mClient.call(METHOD_ADD_CALLBACK, EMPTY_ARG, extras); 136 } catch (RemoteException e) { 137 throw new RuntimeException(e); 138 } 139 } 140 141 @Override close()142 public void close() { 143 mClient.close(); 144 } 145 146 /** 147 * @param uri Content provider URI for cross-process event log collecting. 148 */ create(String tag, Context context, Uri uri)149 public static EventLogClient create(String tag, Context context, Uri uri) { 150 final ContentProviderClient client = context.getContentResolver() 151 .acquireContentProviderClient(uri); 152 if (client == null) { 153 throw new RuntimeException("Unable to acquire " + uri); 154 } 155 return new EventLogClient(client, tag); 156 } 157 } 158 159 @Override call(String method, String arg, Bundle extras)160 public Bundle call(String method, String arg, Bundle extras) { 161 if (!METHOD_ADD_CALLBACK.equals(method)) { 162 throw new UnsupportedOperationException(); 163 } 164 onActivityCallback(extras.getString(EXTRA_KEY_TAG), extras.getString(method)); 165 return null; 166 } 167 168 @Override onCreate()169 public boolean onCreate() { 170 return true; 171 } 172 173 @Override query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)174 public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, 175 String sortOrder) { 176 return null; 177 } 178 179 @Override getType(Uri uri)180 public String getType(Uri uri) { 181 return null; 182 } 183 184 @Override insert(Uri uri, ContentValues values)185 public Uri insert(Uri uri, ContentValues values) { 186 return null; 187 } 188 189 @Override delete(Uri uri, String selection, String[] selectionArgs)190 public int delete(Uri uri, String selection, String[] selectionArgs) { 191 return 0; 192 } 193 194 @Override update(Uri uri, ContentValues values, String selection, String[] selectionArgs)195 public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { 196 return 0; 197 } 198 } 199