I've got a very weird problem and is difficult to describe so please read carefully the assumptions before the answer to avoid jumping into something I already know isn't
1 - I've got an android app which login from my server
2 - after a successful login I instantiate a Singleton API which will be shared across the activities in order to make the requests to the server
3 - Isn't possible to use the app without login
4 - in my login activity I have a very clean condition
if(APIFacade.getInstance() != null){
startActivity(new Intent(this,MainActivity.class));
finish();
}else {/*error handling*/}
5 - there is just one call to startActivity method in the whole LoginActivityclass so isn't possible to start MainActivity without check if the facade is null
6 - it isn't possible to set APIFacade.INSTANCE to null after instantiating it
But even with all these conditions sometimes users get NullPointerException on MainActivity when the app tries to make the first call to API after login
String url = APIFacade.getInstance().getProfilePicUrl(); //throws nullpointerexception on 5% of the times
APIFacade class is like this:
public class APIFacade {
private static APIFacade INSTANCE = null;
#WorkerThread
public APIFacade(Object i, final boolean preLoad) {
INSTANCE = this;
//other stuff
}
public static APIFacade getInstance() {
return INSTANCE;
}
}
I'm not able to reproduce the problem in the development environment so I just know it happens due to the crashlytics dashboard on firebase...
I believe my code has no leak to lead this situation, so the only theory I got is: Android is cleaning some variables from memory when my app goes background...
I know android naturally does it for activities, but singletons?
and if yes what can I do to solve it?
Well I found the answer myself thanks to a great article: https://medium.com/#davethomas_9528/please-dont-use-singletons-to-persist-state-on-android-7bac9bc78b29
Briefly:
Everybody says that singleston on android are attached to the application life, so will only be released if the application is killed
THIS IS TRUE
what you dont hear, is that the application can be killed by the SO without user interaction (to release memory for foreground apps) and in this scenario when the user tries to come back to your app it will restart from the last used activity and not from the launcher activity.
Related
As soon as I receive a push notification from my app I want to trigger the KeyguardManager to launch the fingerprint/pass code screen to open the phone from lock screen so that person can enter the phone and unlock the device.
I want to trigger this programmatically similar to when we click on any notification from lock screen we get the fingerprint/pass-code screen.
I did a lot of RnD but didn't find any solution, this is one of the challenging use case task given to me in class, I have been exploring a lot from quite few weeks with no success at all.
Did tried Broadcast receiver with BiometricManager and many things with no success, any lead will be very helpful.
As soon as you receive push message, onNotificationReceived() (or some other method if you use some 3rd party libs) method gets called as below. from there, you can launch your Main screen where you have written biometric/unlocking code.
class MyReceiver : PushReceiver {
override fun onNotificationReceived(message: Message) : Boolean {
//Launch your MainActivity where you can show Unlock screen.
return super.onNotificationReceived(message);
}
}
My apps on the Google Play store are affected by the Intent Redirection Vulnerability (see article https://support.google.com/faqs/answer/9267555)
I implemented "Option 2" on the recommended solutions yet the warning is still being reported.
I've tried several different ways of verifying the calling activity, but nothing passes whatever check they're doing.
Here's their recommended solution:
// check if the originating Activity is from trusted package
if (getCallingActivity().getPackageName().equals(“known”)) {
Intent intent = getIntent();
// extract the nested Intent
Intent forward = (Intent) intent.getParcelableExtra(“key”);
// redirect the nested Intent
startActivity(forward);
}
And here's my code:
public void onFinish() {
finish();
// check if the originating Activity is from trusted package
if (getCallingActivity() != null &&
getCallingActivity().getPackageName().equals(
PerfectCommon.getAppContext().getPackageName())) {
Intent intent = null;
// extract the nested Intent
intent =
getIntent().getParcelableExtra(BaseActivity.ORIG_ACTIVITY);
if (intent != null){
// redirect the nested Intent
startActivity(intent);
return;
}
}
Intent newIntent;
SplashActivity context = SplashActivity.this;
boolean portrait = PerfectCommon.portraitMode;
ArrayList<Intent> intents = new ArrayList<>();
// Create intent stack for next activities to run, starting w/ last
newIntent = new Intent(context, portrait ? MainActivity.P.class : MainActivity.class);
newIntent.setData(getIntent().getData());
intents.add(newIntent);
startActivities(intents.toArray(new Intent[intents.size()]));
}
};
The code should check to see if the calling activity is a trusted source, then use the intent; if it isn't then a different intent is used.
However, when I publish the app on the Google Play store it says that the vulnerability still exists in this bit of code.
This is legacy code and has been working fine up to now, so I prefer not to make large change to get past this, rather just need to pass whatever static checker is being used.
I ran into this problem as well, and it seems that the confusion is caused by two separate problems, combined. First, their suggested implementation for Option 2 is incomplete due to that method being nullable. Second, they reject the entire app update, if ANY of the remaining APKs is considered vulnerable.
1.
Google confused us by continuing to reject our app updates, even though we believed we had solved the problem. However, you should pay attention to the versionCode specified in the rejection email -- because they might not be rejecting your latest app version!
In our case, we had an old (vulnerable) app version in production (say, versionCode=100) track, and a first-attempt at fixing the issue in the Beta track (versionCode=150) (see #2 below). When we released a second-attempt at fixing the issue (versionCode=200), Google still sent us the rejection email, but the email still specifically cited versionCode 150, NOT 200.
The reason is because the 150 version was in the beta track, and the 200 version went straight to Production, at 5% rollout (did not replace the Beta version). So technically the 150 version was still accessible in the play store to beta users, and that was the reason for rejecting our 200 update. Once they rejected the update due to the 150 APK being available still, the 200 update was also halted, and we had no choice but to make another updated versionCode.
Once we deactivated the 150 version in the beta track, and re-released the 200 app to production, the rejection went away and we confirmed that the newer version was being distributed to users.
2.
Separately, the reason that the first attempt (150) did not resolve the issue, is because of how we were implementing their Option 2 solution. Their solution (which you linked to above)[1]:
if (getCallingActivity().getPackageName().equals(“known”)) {
but they do not consider the fact that getCallingActivity() is nullable. So in our first attempt, we were using:
if (getCallingActivity() == null || getCallingActivity().getPackageName().equals(BuildConfig.APPLICATION_ID)
and that is still vulnerable, hence why our 150 update was rejected. The accepted solution (versionCode 200 in the above example) flipped that logic:
if (getCallingActivity() != null && getCallingActivity().getPackageName().equals(BuildConfig.APPLICATION_ID))
So once we made that change and replaced the app in the beta track, everything was accepted. You can see all currently accessible app versionCodes in the play console by going to Release Management -> App Releases. Every published version in each track will be listed there.
I'm working on a phone-conference app on Android 7. I found this problem.
When app crash I loose ongoing call control resulting in app closed and voice channel open.
Reopening app result in two ongoing calls.
There are ways to close the first voice call?
I try closing the call at app restart but obviously Android OS don't let me touch it.
the best (still not working) result I achieved is error class extension. that event is fired at crash start.
here is my class CrashKillCall that implements Thread.UncaughtExceptionHandler
public void uncaughtException(Thread t, Throwable e) {
//"the last song kill the audience" by Crash & the boys
Log.e(TAG, "--------------------------------------");
Log.e(TAG,t.getName());
Log.e(TAG,e.getCause().getMessage());
Log.e(TAG, "--------------------------------------");
crashCall.disconnect();
Log.e(TAG,"work?");
}
public static void setCall(Call call){
crashCall=call;
}
the desired result is some way to, or to let system know that i want to, terminate the ongoing or all calls.
thank you for your help.
ended out that i was pointing at wrong Call object. code work, you just need declare an istance of that class as default exception listener and register the right Call
Got asked this today in an Android interview. I answered the usual, you know, intent + startActivity, etc. Interviewer then asked more pointedly,
"Yes, but where is it actually instantiated? You don't call new
Activity anywhere".
Which now that I think about it, I don't really know. He hinted that it used Java reflection, but I dont have a lot of experience with that, and I've only used it to access some variables in the Android SDK.
Can someone explain how Activities are instantiated using reflection, and why? Bonus points for insight into what value the interviewer saw in knowing this.
When an app's launcher icon is clicked on homescreen, following event happens under the android system :
Homescreen/Launcher app sends an intent to start an activity using startActivity()(startActivity() is binder call to ActivityManager)
Activity Manager sends a process fork request using a socket to Zygote.
Zygote forks a new VM instance that loads ActivityThread(Activity thread manages the execution of the main thread in an application process, scheduling and executing activities, broadcasts, and other operations on it as the activity manager requests.).
ActivityThread has real main() for an app.
ActivityThread calls the app's onCreate().
Hence ActivityThread is responsible for instantiating Activity(inside performLaunchActivity method)
Explanation :
If you observe the stacktrace :
android.app.Instrumentation.newActivity(Instrumentation.java:1021)
android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2175)
android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2261)
Code where new activity is instantiated :
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
... //More code
Activity activity = null;
try {
java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
StrictMode.incrementExpectedActivityCount(activity.getClass());
r.intent.setExtrasClassLoader(cl);
r.intent.prepareToEnterProcess();
if (r.state != null) {
r.state.setClassLoader(cl);
}
} catch (Exception e) {
if (!mInstrumentation.onException(activity, e)) {
throw new RuntimeException(
"Unable to instantiate activity " + component
+ ": " + e.toString(), e);
}
}
... //More code
return activity;
}
Instrumentation.java(class will be instantiated for you before any of the application code)
public Activity newActivity(ClassLoader cl, String className,
Intent intent)
throws InstantiationException, IllegalAccessException,
ClassNotFoundException {
return (Activity)cl.loadClass(className).newInstance();
}
The simple way to check the path to the constructor method is to create a temporary project, override constructor in your Activity and place breakpoint there.
You should be able to walk through the all code and find what exactly you want.
As long as you are not in an interview for an Android system developer (kernel hacker, ...) the answer is simply: That is an implementation detail of the Android framework a normal Android developer should not need to care about because of the abstraction and layer principle and it can be looked up in the rare case you would really need to know it.
Android core is responsible to manage de activity instantiation, and manage it into his activity lifecycle.
The android system takes care about calling all the events you can control in your class in the correct order, add the activity to the stack and many other things.
When you call startActivity, Android core takes control and makes an activity instance (or reuse a previous one if match) and starts to call activity lifecycle events
You can see it here: http://developer.android.com/reference/android/app/Activity.html in Activity Lifecycle part
I'm making an android app that test if certain security features on your phone are enabled. For example, if you have password log in enabled or if your data is encrypted on your phone.
For some reason, the app has to be ran twice to test and see if these security features are enabled on the phone or not, and this is the problem I'm trying to solve. I'd like it to test and see if the security features are enabled when the app is created and the first time the app is run, not the second time it is run.
I test if these features are enabled in the onStart() function in my MainActivity file. I included the functions code below:
#Override
#TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
#SuppressLint("NewApi")
public void onStart()
{
super.onStart();
//determine if phone uses lock pattern
//It returns 1 if pattern lock enabled and 0 if pin/password password enabled
ContentResolver cr = getBaseContext().getContentResolver();
lockPatternEnable = Settings.Secure.getInt(cr, Settings.Secure.LOCK_PATTERN_ENABLED, 0);//Settings.System
//returns 1 if pin/password protected. 0 if not
KeyguardManager keyguardManager = (KeyguardManager) getBaseContext().getSystemService(Context.KEYGUARD_SERVICE);
if( keyguardManager.isKeyguardSecure())
{
//it is pin or password protected
pinPasswordEnable=1;
}
else
{
//it is not pin or password protected
pinPasswordEnable=0;
}//http://stackoverflow.com/questions/6588969/device-password-in-android-is-existing-or-not/18716253#18716253
//determine if adb is enabled. works
adb=Settings.Global.getInt(cr, Settings.Global.ADB_ENABLED, 0);
//determine if bluetooth is enabled.works
bluetooth=Settings.Global.getInt(cr, Settings.Global.BLUETOOTH_ON, 0);
//Settings.System BLUETOOTH_DISCOVERABILITY
//determine if wifi is enabled. works
WifiManager wifi = (WifiManager)getSystemService(Context.WIFI_SERVICE);
if (wifi.isWifiEnabled())
{
//wifi is enabled
wifiInt=1;
}
else
wifiInt=0;
//determine if data is encrypted
getDeviceEncryptionencryption();
//determine if gps enabled
}//end of onStart() function
If any more code needs to be posted to answer this question, just let me know, and thanks for your help. Maybe the issue has something to do with the super.onStart();
Does anyone think that a splash loading screen might help solve the issue?
super.onStart(); is fine. Splash screen will not help.
From your code I do not see how you determine how many times it ran.
You also mention testing - is it manual testing or you use any framework? Maybe your framework has some init method which runs before each run and it makes this extra call for onStart().
Issues is not in this code. Use debugger or logcat and figure out who calls you twice and, as #nasch had asked, what happens at first run.
Still, real question to help you remains - what do you mean "call twice". Is it you manually clicking app icon twice or is it some testing framework calls your app twice. Both cases are clear to solve.