SingleTask activity, but opened through two different applications - android

This image was quite helpful for understanding the
functionality offered by the launhmode singleTask, taken from here
however, I understood this in case of the same application, I am having issues understanding
what if both tasks belong to two different Applications
Confusing Scenario(fictional),
I was going through an app and the app offered an action to send
emails, I selected 'send email' option.
My phone's default 'email app' will be picked and its activity (which is
declared as singletask) will be opened.
While I was typing my email content, I switched to some chat app and
the app gets crashed and offered me an option to report an issue
over email to the developer, Now when I will select 'Report' , my email
app(which is the same default email app ) will be opened.
Now as the Email app's root activity is singletask, will my content
which I wrote will be visible to me?
The main thing is this time, the tasks/stacks belong to two different apps.

Even though you are using 2 different applications, it will work in the expected way:
if your singleTask activity already exists, that copy will be used, with the method onNewIntent() being called
if it does not exist, it will be launched as per normal
More technically, reproducing the definition from your link:
The system creates a new task and instantiates the activity at the
root of the new task. However, if an instance of the activity already
exists in a separate task, the system routes the intent to the
existing instance through a call to its onNewIntent() method, rather
than creating a new instance. Only one instance of the activity can
exist at a time.
This can easily be verified by making an activity a target for sharing text and singleTask in the manifest:
<activity
android:name=".MainActivity"
android:launchMode="singleTask">
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/plain" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND_MULTIPLE" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="image/*" />
</intent-filter>
</activity>
Now add some logging to the onCreate() and onNewIntent() methods and do some scenario testing.
Something I found particularly useful when testing the various launchmodes is the following ADB command:
adb dumpsys activity activities
This outputs a lot of text (it may help to reboot the phone before doing this - adb reboot) showing details of the activity task stacks. This can be used to show you that your singleTask activity "rehomes" itself as it gets launched via different applications.
As for the question about the emails, I think that will depend on which email client you are using, but I would hope that they handle the onNewIntent() method correctly, and save the current draft before displaying your new email.

Related

Flutter App Cloned with Nfc App Logo when launching via Ndef intent

I want to launch my Flutter Android App automatically, after an Ndef Tag has been discovered. I'm using an Intent filter to achieve this.
All works fine, apart that when launched via the Ndef Tag, the App has an Nfc App Icon.
Also, if the app was already opened manually before, it will have the App open twice:
a) The manually opened one with the correct logo
b) The Ndef Tag launched one
Screenshot with two instances of the app:
To exclude any side effects, I did a new Flutter project from scratch and only did the following changes in ...\android\app\src\main\AndroidManifest.xml:
Added this right after the <manifest> tag:
<uses-permission android:name="android.permission.NFC"/>
And after the existing
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
I added this filter to launch the app:
<intent-filter>
<action android:name="android.nfc.action.NDEF_DISCOVERED" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="https" android:host="developer.android.com" android:pathPrefix="/test" />
</intent-filter>
The Nfc Tag only has one record of type Uri, with the content https://developer.android.com/test
How can I avoid the app clone, and just always open the same App instance with the correct App logo?
To rely on the basic intent nfc functionality you need to add android:launchMode="singleTop" option to the Activity with the NFC intent filter in the Manifest.
From the docs
the "standard" and "singleTop" modes differ from each other in just one respect: Every time there's a new intent for a "standard" activity, a new instance of the class is created to respond to that intent. Each instance handles a single intent. Similarly, a new instance of a "singleTop" activity may also be created to handle a new intent. However, if the target task already has an existing instance of the activity at the top of its stack, that instance will receive the new intent (in an onNewIntent() call); a new instance is not created
As standard is the default launchmode then a new instance of the Activity is created when the NFC is scanned every time, hence multiple instances.
With singletop it will re-use one that is at the top of the stack BUT you will have to handle the processing the data in the onNewIntent method as well as where you are processing it now.
Using the method does have the downside that your App with be paused and resumed to receive the NFC data, so your App has to pause and resume correctly. It is generally better to use one of the Foreground NFC methods like enableReaderMode instead of the basic intent methods, for Flutter there are various flutter packages to do this.

Android Vulnerability Exported activity=true

I have an application that has only 1 activity "MainActivity" and it must be divided in fragments, because there are 3 windows, one of them is the user details, and this activity (the main activity) has exported=true.
I want to know if this entails a security flaw in an Android APK:
I log in the application
I start the activity with drozer: run app.activity.start --component com.member com.member.MainActivity
I can see the user details, but not in the first display, I need to press another tab, which must be another fragment inside the MainActivity.
Is it the normal behavior?
It is true that exported activities can be used by apps with a different uid, but the user must be logged in, which problem does it entail?
Regards!
Is it the normal behavior?
If by "the normal behavior" you mean that exported activities can be started by third-party apps, then yes.
It is true that exported activities can be used by apps with a different uid
If by "used" you mean "started", then yes.
For example, this activity of yours probably has this <intent-filter>:
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
This says "hey, anything that is a launcher, please include me in your roster of launchable activities!". The launcher is how most Android device owners start apps. If your activity is not exported, then the launcher would not be able to start your activity, and as a result the user will not be able to use your app.
Note that having an <intent-filter> on an <activity> automatically sets android:exported to true — you do not need to declare this manually.

POC - Start Service on boot without an Activity - Android 4+

This question may seem a trivial but I've been struggling with it.
I'm trying to start a service on boot and everything works fine if I start it at least once from the mainActivity (launch activity), something like:
AndroidManifest.xml
...
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
...
<activity
android:name="com.example.mainActivity"
android:label="#string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<receiver android:name="com.example.bootReceiver" android:enabled="true" android:exported="false">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
<action android:name="android.intent.action.QUICKBOOT_POWERON" />
</intent-filter>
</receiver>
<service android:name="com.example.someService" android:enabled="true" android:exported="false"/>
...
bootReceiver.java
...
startService(new Intent(getApplicationContext(), com.example.someService.class));
...
mainActivity.java
...
startService(new Intent(getApplicationContext(), com.example.someService.class));
...
With the above code I'm able to run the service after every boot without any problems.
As a POC, I'm trying to start a service without any activity, or at least without the mainActivity, just by declaring the service on the AndroidManifest.xml or by creating new activity (invisible?) that is launched at the same time as the default launcher activity.
As far a I know, this isn't possible on android 3+ (4+?) due to security reasons.
Is there any way to achieve this ?
Can I start 2 activities from the AndroiManifest.xml when the user launches the app?
I'm sorry if my question isn't 100% clear, I've tried to explain it the best I could, if you cannot understand it, please leave a comment bellow. Tks.
As far a I know, this isn't possible on android 3+ (4+?) due to security reasons.
Android 3.1, actually, to help prevent drive-by malware.
Is there any way to achieve this ?
Something has to use an explicit Intent to invoke some form of IPC on one of your components, to move the app out of the so-called "stopped state" that is preventing you from receiving the broadcast. So, something needs to either:
start one of your activities via an explicit Intent, or
start one of your services via an explicit Intent, or
send a broadcast to one of your receivers via an explicit Intent
(I don't know if trying to connect to a ContentProvider will work, though arguably it should)
The key is the explicit Intent. That's why invoking an activity from a home screen launcher works, because the Intent used to start your activity will be an explicit one.
However, in the absence of such an activity, you would need to find something else that would use an explicit Intent to invoke one of your components. Certain specialized services (e.g., an input method) probably get invoked with an explicit Intent if and when the user activates that app's capability via the system Settings app. If you're a plugin for some other app, that other app might use an explicit Intent to work with one of your components. You can ask the user to install the Android SDK, learn how to use the command line, and invoke an adb shell am command to start one of your components. And that's about all I can think of off the top of my head. None are exactly general purpose solutions.
or by creating new activity (invisible?) that is launched at the same time as the default launcher activity
I have no idea what you think that would achieve. If the user starts up your launcher activity, you're already out of the stopped state and will receive broadcasts as normal.

Android app with custom URL scheme launching from within Chrome?

I created a basic Android app from the Eclipse wizard. I then added the following intent filter to AndroidManifest.xml, after the existing one. This makes it support a custom "sample://" URL scheme:
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="sample" />
</intent-filter>
If I run Chrome or the default browser, and click a "sample://" link, it launches my app. However, if I look at the task switcher, my app isn't listed. Instead, Chrome is shown, with my app's screen shot.
Why is this? Can it be fixed? I'm running Android 4.2.2 on a Galaxy Nexus phone.
I notice that if I add android:launchMode="singleInstance" to the activity, it opens in a separate app. But the docs say this is "not recommended for general use". Why not?
The reason why you Activity appears in Recent Apps as Chrome is because it now belongs to the Chrome task, because it was launched from there.
As you noticed android:launchMode="singleInstance" solves your problem, however it is not recommended or discouraged because it would brake the user experience and navigation and how users expect your application to behave.
Fortunately, I think there's a way of specifying Intent flags in your HREF, try something like this:
<A HREF="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.LAUNCHER;launchFlags=0x10000000;component=com.example.package/.MyActivity;end" />
in the previous example launchFlags=FLAG_ACTIVITY_NEW_TASK. This flag is generally used by activities that want to present a "launcher" style behavior: they give the user a list of separate things that can be done, which otherwise run completely independently of the activity launching them.

Activity exported=false listed in activity chooser

I have two similar applications (one free, one paid).
An activity is defined with exported="false"
<activity
android:name=".MyActivity"
android:exported="false"
android:noHistory="true" >
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="vnd.android.cursor.item/vnd.mine" />
</intent-filter>
</activity>
When I call startActivity with the appropriate implicit intent from the free app, the activity picker appears.
I don't understand why the activity from the paid app appears, since it is exported="false"
I suppose I can add an intent filter based on the URL, but my question is: why does the activity from the other app appear when the doc reads
Whether or not the activity can be launched by components of other applications
I don't understand why the activity from the paid app appears, since it is exported="false"
Because you have a matching <intent-filter>. Since you do not need the <intent-filter> for a non-exported activity, simply delete it and use an explicit Intent when starting this activity.
why does the activity from the other app appear when the doc reads...
I had the same question and was told that this was expected behavior and the bug is in our app for having a useless <intent-filter>. Quoting Dianne Hackborn:
I would generally consider this a bug in the app -- if you have an activity that you aren't allowing other apps to launch, why the heck are you publishing an intent filter that they will match to try to launch? The security of the activity (whether it is not exported or requires a permission) is not part of intent matching. ...this scenario (publishing an activity that matches intents other applications will use but then restricting it to not be launchable by other applications) is not useful if not outright broken.

Categories

Resources