Home | History | Annotate | Download | only in lifecycle
      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