Home | History | Annotate | Download | only in app
      1 /*
      2  * Copyright (C) 2013 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.app;
     18 
     19 import static android.app.ActivityManager.START_CANCELED;
     20 
     21 import android.content.Context;
     22 import android.content.ContextWrapper;
     23 import android.content.IIntentSender;
     24 import android.content.Intent;
     25 import android.content.IntentSender;
     26 import android.graphics.SurfaceTexture;
     27 import android.os.IBinder;
     28 import android.os.Message;
     29 import android.os.OperationCanceledException;
     30 import android.os.RemoteException;
     31 import android.util.AttributeSet;
     32 import android.util.DisplayMetrics;
     33 import android.util.Log;
     34 import android.view.InputDevice;
     35 import android.view.InputEvent;
     36 import android.view.MotionEvent;
     37 import android.view.Surface;
     38 import android.view.TextureView;
     39 import android.view.TextureView.SurfaceTextureListener;
     40 import android.view.View;
     41 import android.view.ViewGroup;
     42 import android.view.WindowManager;
     43 import dalvik.system.CloseGuard;
     44 
     45 import java.lang.ref.WeakReference;
     46 import java.util.ArrayDeque;
     47 import java.util.concurrent.Executor;
     48 import java.util.concurrent.BlockingQueue;
     49 import java.util.concurrent.LinkedBlockingQueue;
     50 import java.util.concurrent.ThreadFactory;
     51 import java.util.concurrent.ThreadPoolExecutor;
     52 import java.util.concurrent.TimeUnit;
     53 import java.util.concurrent.atomic.AtomicInteger;
     54 
     55 import com.android.internal.annotations.GuardedBy;
     56 
     57 
     58 /** @hide */
     59 public class ActivityView extends ViewGroup {
     60     private static final String TAG = "ActivityView";
     61     private static final boolean DEBUG = false;
     62 
     63     private static final int MSG_SET_SURFACE = 1;
     64 
     65     private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
     66     private static final int MINIMUM_POOL_SIZE = 1;
     67     private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
     68     private static final int KEEP_ALIVE = 1;
     69 
     70     private static final ThreadFactory sThreadFactory = new ThreadFactory() {
     71         private final AtomicInteger mCount = new AtomicInteger(1);
     72 
     73         public Thread newThread(Runnable r) {
     74             return new Thread(r, "ActivityView #" + mCount.getAndIncrement());
     75         }
     76     };
     77 
     78     private static final BlockingQueue<Runnable> sPoolWorkQueue =
     79             new LinkedBlockingQueue<Runnable>(128);
     80 
     81     /**
     82      * An {@link Executor} that can be used to execute tasks in parallel.
     83      */
     84     private static final Executor sExecutor = new ThreadPoolExecutor(MINIMUM_POOL_SIZE,
     85             MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
     86 
     87 
     88     private static class SerialExecutor implements Executor {
     89         private final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
     90         private Runnable mActive;
     91 
     92         public synchronized void execute(final Runnable r) {
     93             mTasks.offer(new Runnable() {
     94                 public void run() {
     95                     try {
     96                         r.run();
     97                     } finally {
     98                         scheduleNext();
     99                     }
    100                 }
    101             });
    102             if (mActive == null) {
    103                 scheduleNext();
    104             }
    105         }
    106 
    107         protected synchronized void scheduleNext() {
    108             if ((mActive = mTasks.poll()) != null) {
    109                 sExecutor.execute(mActive);
    110             }
    111         }
    112     }
    113 
    114     private final SerialExecutor mExecutor = new SerialExecutor();
    115 
    116     private final int mDensityDpi;
    117     private final TextureView mTextureView;
    118 
    119     @GuardedBy("mActivityContainerLock")
    120     private ActivityContainerWrapper mActivityContainer;
    121     private Object mActivityContainerLock = new Object();
    122 
    123     private Activity mActivity;
    124     private int mWidth;
    125     private int mHeight;
    126     private Surface mSurface;
    127     private int mLastVisibility;
    128     private ActivityViewCallback mActivityViewCallback;
    129 
    130 
    131     public ActivityView(Context context) {
    132         this(context, null);
    133     }
    134 
    135     public ActivityView(Context context, AttributeSet attrs) {
    136         this(context, attrs, 0);
    137     }
    138 
    139     public ActivityView(Context context, AttributeSet attrs, int defStyle) {
    140         super(context, attrs, defStyle);
    141 
    142         while (context instanceof ContextWrapper) {
    143             if (context instanceof Activity) {
    144                 mActivity = (Activity)context;
    145                 break;
    146             }
    147             context = ((ContextWrapper)context).getBaseContext();
    148         }
    149         if (mActivity == null) {
    150             throw new IllegalStateException("The ActivityView's Context is not an Activity.");
    151         }
    152 
    153         try {
    154             mActivityContainer = new ActivityContainerWrapper(
    155                     ActivityManagerNative.getDefault().createVirtualActivityContainer(
    156                             mActivity.getActivityToken(), new ActivityContainerCallback(this)));
    157         } catch (RemoteException e) {
    158             throw new RuntimeException("ActivityView: Unable to create ActivityContainer. "
    159                     + e);
    160         }
    161 
    162         mTextureView = new TextureView(context);
    163         mTextureView.setSurfaceTextureListener(new ActivityViewSurfaceTextureListener());
    164         addView(mTextureView);
    165 
    166         WindowManager wm = (WindowManager)mActivity.getSystemService(Context.WINDOW_SERVICE);
    167         DisplayMetrics metrics = new DisplayMetrics();
    168         wm.getDefaultDisplay().getMetrics(metrics);
    169         mDensityDpi = metrics.densityDpi;
    170 
    171         mLastVisibility = getVisibility();
    172 
    173         if (DEBUG) Log.v(TAG, "ctor()");
    174     }
    175 
    176     @Override
    177     protected void onLayout(boolean changed, int l, int t, int r, int b) {
    178         mTextureView.layout(0, 0, r - l, b - t);
    179     }
    180 
    181     @Override
    182     protected void onVisibilityChanged(View changedView, final int visibility) {
    183         super.onVisibilityChanged(changedView, visibility);
    184 
    185         if (mSurface != null && (visibility == View.GONE || mLastVisibility == View.GONE)) {
    186             if (DEBUG) Log.v(TAG, "visibility changed; enqueing runnable");
    187             final Surface surface = (visibility == View.GONE) ? null : mSurface;
    188             setSurfaceAsync(surface, mWidth, mHeight, mDensityDpi, false);
    189         }
    190         mLastVisibility = visibility;
    191     }
    192 
    193     private boolean injectInputEvent(InputEvent event) {
    194         return mActivityContainer != null && mActivityContainer.injectEvent(event);
    195     }
    196 
    197     @Override
    198     public boolean onTouchEvent(MotionEvent event) {
    199         return injectInputEvent(event) || super.onTouchEvent(event);
    200     }
    201 
    202     @Override
    203     public boolean onGenericMotionEvent(MotionEvent event) {
    204         if (event.isFromSource(InputDevice.SOURCE_CLASS_POINTER)) {
    205             if (injectInputEvent(event)) {
    206                 return true;
    207             }
    208         }
    209         return super.onGenericMotionEvent(event);
    210     }
    211 
    212     @Override
    213     public void onAttachedToWindow() {
    214         if (DEBUG) Log.v(TAG, "onAttachedToWindow(): mActivityContainer=" + mActivityContainer +
    215                 " mSurface=" + mSurface);
    216     }
    217 
    218     @Override
    219     public void onDetachedFromWindow() {
    220         if (DEBUG) Log.v(TAG, "onDetachedFromWindow(): mActivityContainer=" + mActivityContainer +
    221                 " mSurface=" + mSurface);
    222     }
    223 
    224     public boolean isAttachedToDisplay() {
    225         return mSurface != null;
    226     }
    227 
    228     public void startActivity(Intent intent) {
    229         if (mActivityContainer == null) {
    230             throw new IllegalStateException("Attempt to call startActivity after release");
    231         }
    232         if (mSurface == null) {
    233             throw new IllegalStateException("Surface not yet created.");
    234         }
    235         if (DEBUG) Log.v(TAG, "startActivity(): intent=" + intent + " " +
    236                 (isAttachedToDisplay() ? "" : "not") + " attached");
    237         if (mActivityContainer.startActivity(intent) == START_CANCELED) {
    238             throw new OperationCanceledException();
    239         }
    240     }
    241 
    242     public void startActivity(IntentSender intentSender) {
    243         if (mActivityContainer == null) {
    244             throw new IllegalStateException("Attempt to call startActivity after release");
    245         }
    246         if (mSurface == null) {
    247             throw new IllegalStateException("Surface not yet created.");
    248         }
    249         if (DEBUG) Log.v(TAG, "startActivityIntentSender(): intentSender=" + intentSender + " " +
    250                 (isAttachedToDisplay() ? "" : "not") + " attached");
    251         final IIntentSender iIntentSender = intentSender.getTarget();
    252         if (mActivityContainer.startActivityIntentSender(iIntentSender) == START_CANCELED) {
    253             throw new OperationCanceledException();
    254         }
    255     }
    256 
    257     public void startActivity(PendingIntent pendingIntent) {
    258         if (mActivityContainer == null) {
    259             throw new IllegalStateException("Attempt to call startActivity after release");
    260         }
    261         if (mSurface == null) {
    262             throw new IllegalStateException("Surface not yet created.");
    263         }
    264         if (DEBUG) Log.v(TAG, "startActivityPendingIntent(): PendingIntent=" + pendingIntent + " "
    265                 + (isAttachedToDisplay() ? "" : "not") + " attached");
    266         final IIntentSender iIntentSender = pendingIntent.getTarget();
    267         if (mActivityContainer.startActivityIntentSender(iIntentSender) == START_CANCELED) {
    268             throw new OperationCanceledException();
    269         }
    270     }
    271 
    272     public void release() {
    273         if (DEBUG) Log.v(TAG, "release() mActivityContainer=" + mActivityContainer +
    274                 " mSurface=" + mSurface);
    275         if (mActivityContainer == null) {
    276             Log.e(TAG, "Duplicate call to release");
    277             return;
    278         }
    279         synchronized (mActivityContainerLock) {
    280             mActivityContainer.release();
    281             mActivityContainer = null;
    282         }
    283 
    284         if (mSurface != null) {
    285             mSurface.release();
    286             mSurface = null;
    287         }
    288 
    289         mTextureView.setSurfaceTextureListener(null);
    290     }
    291 
    292     private void setSurfaceAsync(final Surface surface, final int width, final int height,
    293             final int densityDpi, final boolean callback) {
    294         mExecutor.execute(new Runnable() {
    295             public void run() {
    296                 try {
    297                     synchronized (mActivityContainerLock) {
    298                         if (mActivityContainer != null) {
    299                             mActivityContainer.setSurface(surface, width, height, densityDpi);
    300                         }
    301                     }
    302                 } catch (RemoteException e) {
    303                     throw new RuntimeException(
    304                         "ActivityView: Unable to set surface of ActivityContainer. ",
    305                         e);
    306                 }
    307                 if (callback) {
    308                     post(new Runnable() {
    309                         @Override
    310                         public void run() {
    311                             if (mActivityViewCallback != null) {
    312                                 if (surface != null) {
    313                                     mActivityViewCallback.onSurfaceAvailable(ActivityView.this);
    314                                 } else {
    315                                     mActivityViewCallback.onSurfaceDestroyed(ActivityView.this);
    316                                 }
    317                             }
    318                         }
    319                     });
    320                 }
    321             }
    322         });
    323     }
    324 
    325     /**
    326      * Set the callback to use to report certain state changes.
    327      *
    328      * Note: If the surface has been created prior to this call being made, then
    329      * ActivityViewCallback.onSurfaceAvailable will be called from within setCallback.
    330      *
    331      *  @param callback The callback to report events to.
    332      *
    333      * @see ActivityViewCallback
    334      */
    335     public void setCallback(ActivityViewCallback callback) {
    336         mActivityViewCallback = callback;
    337 
    338         if (mSurface != null) {
    339             mActivityViewCallback.onSurfaceAvailable(this);
    340         }
    341     }
    342 
    343     public static abstract class ActivityViewCallback {
    344         /**
    345          * Called when all activities in the ActivityView have completed and been removed. Register
    346          * using {@link ActivityView#setCallback(ActivityViewCallback)}. Each ActivityView may
    347          * have at most one callback registered.
    348          */
    349         public abstract void onAllActivitiesComplete(ActivityView view);
    350         /**
    351          * Called when the surface is ready to be drawn to. Calling startActivity prior to this
    352          * callback will result in an IllegalStateException.
    353          */
    354         public abstract void onSurfaceAvailable(ActivityView view);
    355         /**
    356          * Called when the surface has been removed. Calling startActivity after this callback
    357          * will result in an IllegalStateException.
    358          */
    359         public abstract void onSurfaceDestroyed(ActivityView view);
    360     }
    361 
    362     private class ActivityViewSurfaceTextureListener implements SurfaceTextureListener {
    363         @Override
    364         public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int width,
    365                 int height) {
    366             if (mActivityContainer == null) {
    367                 return;
    368             }
    369             if (DEBUG) Log.d(TAG, "onSurfaceTextureAvailable: width=" + width + " height="
    370                     + height);
    371             mWidth = width;
    372             mHeight = height;
    373             mSurface = new Surface(surfaceTexture);
    374             setSurfaceAsync(mSurface, mWidth, mHeight, mDensityDpi, true);
    375         }
    376 
    377         @Override
    378         public void onSurfaceTextureSizeChanged(SurfaceTexture surfaceTexture, int width,
    379                 int height) {
    380             if (mActivityContainer == null) {
    381                 return;
    382             }
    383             if (DEBUG) Log.d(TAG, "onSurfaceTextureSizeChanged: w=" + width + " h=" + height);
    384         }
    385 
    386         @Override
    387         public boolean onSurfaceTextureDestroyed(SurfaceTexture surfaceTexture) {
    388             if (mActivityContainer == null) {
    389                 return true;
    390             }
    391             if (DEBUG) Log.d(TAG, "onSurfaceTextureDestroyed");
    392             mSurface.release();
    393             mSurface = null;
    394             setSurfaceAsync(null, mWidth, mHeight, mDensityDpi, true);
    395             return true;
    396         }
    397 
    398         @Override
    399         public void onSurfaceTextureUpdated(SurfaceTexture surfaceTexture) {
    400 //            Log.d(TAG, "onSurfaceTextureUpdated");
    401         }
    402 
    403     }
    404 
    405     private static class ActivityContainerCallback extends IActivityContainerCallback.Stub {
    406         private final WeakReference<ActivityView> mActivityViewWeakReference;
    407 
    408         ActivityContainerCallback(ActivityView activityView) {
    409             mActivityViewWeakReference = new WeakReference<>(activityView);
    410         }
    411 
    412         @Override
    413         public void setVisible(IBinder container, boolean visible) {
    414             if (DEBUG) Log.v(TAG, "setVisible(): container=" + container + " visible=" + visible +
    415                     " ActivityView=" + mActivityViewWeakReference.get());
    416         }
    417 
    418         @Override
    419         public void onAllActivitiesComplete(IBinder container) {
    420             final ActivityView activityView = mActivityViewWeakReference.get();
    421             if (activityView != null) {
    422                 final ActivityViewCallback callback = activityView.mActivityViewCallback;
    423                 if (callback != null) {
    424                     final WeakReference<ActivityViewCallback> callbackRef =
    425                             new WeakReference<>(callback);
    426                     activityView.post(new Runnable() {
    427                         @Override
    428                         public void run() {
    429                             ActivityViewCallback callback = callbackRef.get();
    430                             if (callback != null) {
    431                                 callback.onAllActivitiesComplete(activityView);
    432                             }
    433                         }
    434                     });
    435                 }
    436             }
    437         }
    438     }
    439 
    440     private static class ActivityContainerWrapper {
    441         private final IActivityContainer mIActivityContainer;
    442         private final CloseGuard mGuard = CloseGuard.get();
    443         boolean mOpened; // Protected by mGuard.
    444 
    445         ActivityContainerWrapper(IActivityContainer container) {
    446             mIActivityContainer = container;
    447             mOpened = true;
    448             mGuard.open("release");
    449         }
    450 
    451         void attachToDisplay(int displayId) {
    452             try {
    453                 mIActivityContainer.attachToDisplay(displayId);
    454             } catch (RemoteException e) {
    455             }
    456         }
    457 
    458         void setSurface(Surface surface, int width, int height, int density)
    459                 throws RemoteException {
    460             mIActivityContainer.setSurface(surface, width, height, density);
    461         }
    462 
    463         int startActivity(Intent intent) {
    464             try {
    465                 return mIActivityContainer.startActivity(intent);
    466             } catch (RemoteException e) {
    467                 throw new RuntimeException("ActivityView: Unable to startActivity. " + e);
    468             }
    469         }
    470 
    471         int startActivityIntentSender(IIntentSender intentSender) {
    472             try {
    473                 return mIActivityContainer.startActivityIntentSender(intentSender);
    474             } catch (RemoteException e) {
    475                 throw new RuntimeException(
    476                         "ActivityView: Unable to startActivity from IntentSender. " + e);
    477             }
    478         }
    479 
    480         int getDisplayId() {
    481             try {
    482                 return mIActivityContainer.getDisplayId();
    483             } catch (RemoteException e) {
    484                 return -1;
    485             }
    486         }
    487 
    488         boolean injectEvent(InputEvent event) {
    489             try {
    490                 return mIActivityContainer.injectEvent(event);
    491             } catch (RemoteException e) {
    492                 return false;
    493             }
    494         }
    495 
    496         void release() {
    497             synchronized (mGuard) {
    498                 if (mOpened) {
    499                     if (DEBUG) Log.v(TAG, "ActivityContainerWrapper: release called");
    500                     try {
    501                         mIActivityContainer.release();
    502                         mGuard.close();
    503                     } catch (RemoteException e) {
    504                     }
    505                     mOpened = false;
    506                 }
    507             }
    508         }
    509 
    510         @Override
    511         protected void finalize() throws Throwable {
    512             if (DEBUG) Log.v(TAG, "ActivityContainerWrapper: finalize called");
    513             try {
    514                 if (mGuard != null) {
    515                     mGuard.warnIfOpen();
    516                     release();
    517                 }
    518             } finally {
    519                 super.finalize();
    520             }
    521         }
    522 
    523     }
    524 }
    525