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 
execute(final Runnable r)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 
scheduleNext()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 
ActivityView(Context context)131     public ActivityView(Context context) {
132         this(context, null);
133     }
134 
ActivityView(Context context, AttributeSet attrs)135     public ActivityView(Context context, AttributeSet attrs) {
136         this(context, attrs, 0);
137     }
138 
ActivityView(Context context, AttributeSet attrs, int defStyle)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                     ActivityManager.getService().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
onLayout(boolean changed, int l, int t, int r, int b)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
onVisibilityChanged(View changedView, final int visibility)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 
injectInputEvent(InputEvent event)193     private boolean injectInputEvent(InputEvent event) {
194         return mActivityContainer != null && mActivityContainer.injectEvent(event);
195     }
196 
197     @Override
onTouchEvent(MotionEvent event)198     public boolean onTouchEvent(MotionEvent event) {
199         return injectInputEvent(event) || super.onTouchEvent(event);
200     }
201 
202     @Override
onGenericMotionEvent(MotionEvent event)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
onAttachedToWindow()213     public void onAttachedToWindow() {
214         if (DEBUG) Log.v(TAG, "onAttachedToWindow(): mActivityContainer=" + mActivityContainer +
215                 " mSurface=" + mSurface);
216     }
217 
218     @Override
onDetachedFromWindow()219     public void onDetachedFromWindow() {
220         if (DEBUG) Log.v(TAG, "onDetachedFromWindow(): mActivityContainer=" + mActivityContainer +
221                 " mSurface=" + mSurface);
222     }
223 
isAttachedToDisplay()224     public boolean isAttachedToDisplay() {
225         return mSurface != null;
226     }
227 
startActivity(Intent intent)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 
startActivity(IntentSender intentSender)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 
startActivity(PendingIntent pendingIntent)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 
release()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 
setSurfaceAsync(final Surface surface, final int width, final int height, final int densityDpi, final boolean callback)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      */
setCallback(ActivityViewCallback callback)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          */
onAllActivitiesComplete(ActivityView view)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          */
onSurfaceAvailable(ActivityView view)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          */
onSurfaceDestroyed(ActivityView view)359         public abstract void onSurfaceDestroyed(ActivityView view);
360     }
361 
362     private class ActivityViewSurfaceTextureListener implements SurfaceTextureListener {
363         @Override
onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int width, int height)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
onSurfaceTextureSizeChanged(SurfaceTexture surfaceTexture, int width, int height)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
onSurfaceTextureDestroyed(SurfaceTexture surfaceTexture)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
onSurfaceTextureUpdated(SurfaceTexture surfaceTexture)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 
ActivityContainerCallback(ActivityView activityView)408         ActivityContainerCallback(ActivityView activityView) {
409             mActivityViewWeakReference = new WeakReference<>(activityView);
410         }
411 
412         @Override
setVisible(IBinder container, boolean visible)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
onAllActivitiesComplete(IBinder container)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 
ActivityContainerWrapper(IActivityContainer container)445         ActivityContainerWrapper(IActivityContainer container) {
446             mIActivityContainer = container;
447             mOpened = true;
448             mGuard.open("release");
449         }
450 
setSurface(Surface surface, int width, int height, int density)451         void setSurface(Surface surface, int width, int height, int density)
452                 throws RemoteException {
453             mIActivityContainer.setSurface(surface, width, height, density);
454         }
455 
startActivity(Intent intent)456         int startActivity(Intent intent) {
457             try {
458                 return mIActivityContainer.startActivity(intent);
459             } catch (RemoteException e) {
460                 throw new RuntimeException("ActivityView: Unable to startActivity. " + e);
461             }
462         }
463 
startActivityIntentSender(IIntentSender intentSender)464         int startActivityIntentSender(IIntentSender intentSender) {
465             try {
466                 return mIActivityContainer.startActivityIntentSender(intentSender);
467             } catch (RemoteException e) {
468                 throw new RuntimeException(
469                         "ActivityView: Unable to startActivity from IntentSender. " + e);
470             }
471         }
472 
getDisplayId()473         int getDisplayId() {
474             try {
475                 return mIActivityContainer.getDisplayId();
476             } catch (RemoteException e) {
477                 return -1;
478             }
479         }
480 
injectEvent(InputEvent event)481         boolean injectEvent(InputEvent event) {
482             try {
483                 return mIActivityContainer.injectEvent(event);
484             } catch (RemoteException e) {
485                 return false;
486             }
487         }
488 
release()489         void release() {
490             synchronized (mGuard) {
491                 if (mOpened) {
492                     if (DEBUG) Log.v(TAG, "ActivityContainerWrapper: release called");
493                     try {
494                         mIActivityContainer.release();
495                         mGuard.close();
496                     } catch (RemoteException e) {
497                     }
498                     mOpened = false;
499                 }
500             }
501         }
502 
503         @Override
finalize()504         protected void finalize() throws Throwable {
505             if (DEBUG) Log.v(TAG, "ActivityContainerWrapper: finalize called");
506             try {
507                 if (mGuard != null) {
508                     mGuard.warnIfOpen();
509                     release();
510                 }
511             } finally {
512                 super.finalize();
513             }
514         }
515 
516     }
517 }
518