1 /* 2 * Copyright (C) 2010 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 android.compat.annotation.UnsupportedAppUsage; 20 import android.content.Context; 21 import android.content.pm.ActivityInfo; 22 import android.content.pm.PackageManager; 23 import android.content.res.AssetManager; 24 import android.content.res.Configuration; 25 import android.graphics.PixelFormat; 26 import android.os.Build; 27 import android.os.Bundle; 28 import android.os.Looper; 29 import android.os.MessageQueue; 30 import android.util.AttributeSet; 31 import android.view.InputQueue; 32 import android.view.Surface; 33 import android.view.SurfaceHolder; 34 import android.view.View; 35 import android.view.ViewTreeObserver.OnGlobalLayoutListener; 36 import android.view.WindowManager; 37 import android.view.inputmethod.InputMethodManager; 38 39 import dalvik.system.BaseDexClassLoader; 40 41 import java.io.File; 42 43 /** 44 * Convenience for implementing an activity that will be implemented 45 * purely in native code. That is, a game (or game-like thing). There 46 * is no need to derive from this class; you can simply declare it in your 47 * manifest, and use the NDK APIs from there. 48 * 49 * <p>A <a href="https://github.com/googlesamples/android-ndk/tree/master/native-activity">sample 50 * native activity</a> is available in the NDK samples. 51 */ 52 public class NativeActivity extends Activity implements SurfaceHolder.Callback2, 53 InputQueue.Callback, OnGlobalLayoutListener { 54 /** 55 * Optional meta-that can be in the manifest for this component, specifying 56 * the name of the native shared library to load. If not specified, 57 * "main" is used. 58 */ 59 public static final String META_DATA_LIB_NAME = "android.app.lib_name"; 60 61 /** 62 * Optional meta-that can be in the manifest for this component, specifying 63 * the name of the main entry point for this native activity in the 64 * {@link #META_DATA_LIB_NAME} native code. If not specified, 65 * "ANativeActivity_onCreate" is used. 66 */ 67 public static final String META_DATA_FUNC_NAME = "android.app.func_name"; 68 69 private static final String KEY_NATIVE_SAVED_STATE = "android:native_state"; 70 71 private NativeContentView mNativeContentView; 72 private InputMethodManager mIMM; 73 74 @UnsupportedAppUsage 75 private long mNativeHandle; 76 77 private InputQueue mCurInputQueue; 78 private SurfaceHolder mCurSurfaceHolder; 79 80 final int[] mLocation = new int[2]; 81 int mLastContentX; 82 int mLastContentY; 83 int mLastContentWidth; 84 int mLastContentHeight; 85 86 private boolean mDispatchingUnhandledKey; 87 88 private boolean mDestroyed; 89 90 @UnsupportedAppUsage loadNativeCode(String path, String funcname, MessageQueue queue, String internalDataPath, String obbPath, String externalDataPath, int sdkVersion, AssetManager assetMgr, byte[] savedState, ClassLoader classLoader, String libraryPath)91 private native long loadNativeCode(String path, String funcname, MessageQueue queue, 92 String internalDataPath, String obbPath, String externalDataPath, int sdkVersion, 93 AssetManager assetMgr, byte[] savedState, ClassLoader classLoader, String libraryPath); getDlError()94 private native String getDlError(); unloadNativeCode(long handle)95 private native void unloadNativeCode(long handle); onStartNative(long handle)96 private native void onStartNative(long handle); onResumeNative(long handle)97 private native void onResumeNative(long handle); onSaveInstanceStateNative(long handle)98 private native byte[] onSaveInstanceStateNative(long handle); onPauseNative(long handle)99 private native void onPauseNative(long handle); onStopNative(long handle)100 private native void onStopNative(long handle); onConfigurationChangedNative(long handle)101 private native void onConfigurationChangedNative(long handle); onLowMemoryNative(long handle)102 private native void onLowMemoryNative(long handle); onWindowFocusChangedNative(long handle, boolean focused)103 private native void onWindowFocusChangedNative(long handle, boolean focused); onSurfaceCreatedNative(long handle, Surface surface)104 private native void onSurfaceCreatedNative(long handle, Surface surface); onSurfaceChangedNative(long handle, Surface surface, int format, int width, int height)105 private native void onSurfaceChangedNative(long handle, Surface surface, 106 int format, int width, int height); onSurfaceRedrawNeededNative(long handle, Surface surface)107 private native void onSurfaceRedrawNeededNative(long handle, Surface surface); onSurfaceDestroyedNative(long handle)108 private native void onSurfaceDestroyedNative(long handle); onInputQueueCreatedNative(long handle, long queuePtr)109 private native void onInputQueueCreatedNative(long handle, long queuePtr); onInputQueueDestroyedNative(long handle, long queuePtr)110 private native void onInputQueueDestroyedNative(long handle, long queuePtr); onContentRectChangedNative(long handle, int x, int y, int w, int h)111 private native void onContentRectChangedNative(long handle, int x, int y, int w, int h); 112 113 static class NativeContentView extends View { 114 NativeActivity mActivity; 115 NativeContentView(Context context)116 public NativeContentView(Context context) { 117 super(context); 118 } 119 NativeContentView(Context context, AttributeSet attrs)120 public NativeContentView(Context context, AttributeSet attrs) { 121 super(context, attrs); 122 } 123 } 124 125 @Override onCreate(Bundle savedInstanceState)126 protected void onCreate(Bundle savedInstanceState) { 127 String libname = "main"; 128 String funcname = "ANativeActivity_onCreate"; 129 ActivityInfo ai; 130 131 mIMM = getSystemService(InputMethodManager.class); 132 133 getWindow().takeSurface(this); 134 getWindow().takeInputQueue(this); 135 getWindow().setFormat(PixelFormat.RGB_565); 136 getWindow().setSoftInputMode( 137 WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED 138 | WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE); 139 140 mNativeContentView = new NativeContentView(this); 141 mNativeContentView.mActivity = this; 142 setContentView(mNativeContentView); 143 mNativeContentView.requestFocus(); 144 mNativeContentView.getViewTreeObserver().addOnGlobalLayoutListener(this); 145 146 try { 147 ai = getPackageManager().getActivityInfo( 148 getIntent().getComponent(), PackageManager.GET_META_DATA); 149 if (ai.metaData != null) { 150 String ln = ai.metaData.getString(META_DATA_LIB_NAME); 151 if (ln != null) libname = ln; 152 ln = ai.metaData.getString(META_DATA_FUNC_NAME); 153 if (ln != null) funcname = ln; 154 } 155 } catch (PackageManager.NameNotFoundException e) { 156 throw new RuntimeException("Error getting activity info", e); 157 } 158 159 BaseDexClassLoader classLoader = (BaseDexClassLoader) getClassLoader(); 160 String path = classLoader.findLibrary(libname); 161 162 if (path == null) { 163 throw new IllegalArgumentException("Unable to find native library " + libname + 164 " using classloader: " + classLoader.toString()); 165 } 166 167 byte[] nativeSavedState = savedInstanceState != null 168 ? savedInstanceState.getByteArray(KEY_NATIVE_SAVED_STATE) : null; 169 170 mNativeHandle = loadNativeCode(path, funcname, Looper.myQueue(), 171 getAbsolutePath(getFilesDir()), getAbsolutePath(getObbDir()), 172 getAbsolutePath(getExternalFilesDir(null)), 173 Build.VERSION.SDK_INT, getAssets(), nativeSavedState, 174 classLoader, classLoader.getLdLibraryPath()); 175 176 if (mNativeHandle == 0) { 177 throw new UnsatisfiedLinkError( 178 "Unable to load native library \"" + path + "\": " + getDlError()); 179 } 180 super.onCreate(savedInstanceState); 181 } 182 getAbsolutePath(File file)183 private static String getAbsolutePath(File file) { 184 return (file != null) ? file.getAbsolutePath() : null; 185 } 186 187 @Override onDestroy()188 protected void onDestroy() { 189 mDestroyed = true; 190 if (mCurSurfaceHolder != null) { 191 onSurfaceDestroyedNative(mNativeHandle); 192 mCurSurfaceHolder = null; 193 } 194 if (mCurInputQueue != null) { 195 onInputQueueDestroyedNative(mNativeHandle, mCurInputQueue.getNativePtr()); 196 mCurInputQueue = null; 197 } 198 unloadNativeCode(mNativeHandle); 199 super.onDestroy(); 200 } 201 202 @Override onPause()203 protected void onPause() { 204 super.onPause(); 205 onPauseNative(mNativeHandle); 206 } 207 208 @Override onResume()209 protected void onResume() { 210 super.onResume(); 211 onResumeNative(mNativeHandle); 212 } 213 214 @Override onSaveInstanceState(Bundle outState)215 protected void onSaveInstanceState(Bundle outState) { 216 super.onSaveInstanceState(outState); 217 byte[] state = onSaveInstanceStateNative(mNativeHandle); 218 if (state != null) { 219 outState.putByteArray(KEY_NATIVE_SAVED_STATE, state); 220 } 221 } 222 223 @Override onStart()224 protected void onStart() { 225 super.onStart(); 226 onStartNative(mNativeHandle); 227 } 228 229 @Override onStop()230 protected void onStop() { 231 super.onStop(); 232 onStopNative(mNativeHandle); 233 } 234 235 @Override onConfigurationChanged(Configuration newConfig)236 public void onConfigurationChanged(Configuration newConfig) { 237 super.onConfigurationChanged(newConfig); 238 if (!mDestroyed) { 239 onConfigurationChangedNative(mNativeHandle); 240 } 241 } 242 243 @Override onLowMemory()244 public void onLowMemory() { 245 super.onLowMemory(); 246 if (!mDestroyed) { 247 onLowMemoryNative(mNativeHandle); 248 } 249 } 250 251 @Override onWindowFocusChanged(boolean hasFocus)252 public void onWindowFocusChanged(boolean hasFocus) { 253 super.onWindowFocusChanged(hasFocus); 254 if (!mDestroyed) { 255 onWindowFocusChangedNative(mNativeHandle, hasFocus); 256 } 257 } 258 surfaceCreated(SurfaceHolder holder)259 public void surfaceCreated(SurfaceHolder holder) { 260 if (!mDestroyed) { 261 mCurSurfaceHolder = holder; 262 onSurfaceCreatedNative(mNativeHandle, holder.getSurface()); 263 } 264 } 265 surfaceChanged(SurfaceHolder holder, int format, int width, int height)266 public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { 267 if (!mDestroyed) { 268 mCurSurfaceHolder = holder; 269 onSurfaceChangedNative(mNativeHandle, holder.getSurface(), format, width, height); 270 } 271 } 272 surfaceRedrawNeeded(SurfaceHolder holder)273 public void surfaceRedrawNeeded(SurfaceHolder holder) { 274 if (!mDestroyed) { 275 mCurSurfaceHolder = holder; 276 onSurfaceRedrawNeededNative(mNativeHandle, holder.getSurface()); 277 } 278 } 279 surfaceDestroyed(SurfaceHolder holder)280 public void surfaceDestroyed(SurfaceHolder holder) { 281 mCurSurfaceHolder = null; 282 if (!mDestroyed) { 283 onSurfaceDestroyedNative(mNativeHandle); 284 } 285 } 286 onInputQueueCreated(InputQueue queue)287 public void onInputQueueCreated(InputQueue queue) { 288 if (!mDestroyed) { 289 mCurInputQueue = queue; 290 onInputQueueCreatedNative(mNativeHandle, queue.getNativePtr()); 291 } 292 } 293 onInputQueueDestroyed(InputQueue queue)294 public void onInputQueueDestroyed(InputQueue queue) { 295 if (!mDestroyed) { 296 onInputQueueDestroyedNative(mNativeHandle, queue.getNativePtr()); 297 mCurInputQueue = null; 298 } 299 } 300 onGlobalLayout()301 public void onGlobalLayout() { 302 mNativeContentView.getLocationInWindow(mLocation); 303 int w = mNativeContentView.getWidth(); 304 int h = mNativeContentView.getHeight(); 305 if (mLocation[0] != mLastContentX || mLocation[1] != mLastContentY 306 || w != mLastContentWidth || h != mLastContentHeight) { 307 mLastContentX = mLocation[0]; 308 mLastContentY = mLocation[1]; 309 mLastContentWidth = w; 310 mLastContentHeight = h; 311 if (!mDestroyed) { 312 onContentRectChangedNative(mNativeHandle, mLastContentX, 313 mLastContentY, mLastContentWidth, mLastContentHeight); 314 } 315 } 316 } 317 318 @UnsupportedAppUsage setWindowFlags(int flags, int mask)319 void setWindowFlags(int flags, int mask) { 320 getWindow().setFlags(flags, mask); 321 } 322 323 @UnsupportedAppUsage setWindowFormat(int format)324 void setWindowFormat(int format) { 325 getWindow().setFormat(format); 326 } 327 328 @UnsupportedAppUsage showIme(int mode)329 void showIme(int mode) { 330 mIMM.showSoftInput(mNativeContentView, mode); 331 } 332 333 @UnsupportedAppUsage hideIme(int mode)334 void hideIme(int mode) { 335 mIMM.hideSoftInputFromWindow(mNativeContentView.getWindowToken(), mode); 336 } 337 } 338