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