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