How to restrict bound service to be called by particular packages - android

I have written a bound service and I would like this service to be only called from particular app. I do not want other apps to be able to make calls to this service.
The options I know so far are:
Use a permission. There seems to be 3 secured permission, dangerous, signature and signatureOrSystem. Unfortunately, none of these permissions will work for me as I don't want users to accept this permission also both app does not have same signature and these are not system app.
Get app name on service bind or when making a call to service. I looked up a way to do this on stackoverflow here. This unfortunately does not works for me as it always returns the app ID in which the service resides.
Is there any other option for me or I can use the above mentioned options with some change to achieve the desired requirement.
Bound Service Code
public class SampleCommsService extends Service {
private static Messenger messanger;
#Override
public IBinder onBind(Intent intent) {
Log.e("TEST", "package intent: " + intent.getPackage());
String callingApp = MyApplication.getAppContext().getPackageManager().getNameForUid(Binder.getCallingUid());
Log.e("TEST", "onBind - package name: " + callingApp);
return getMyBinder();
}
private synchronized IBinder getMyBinder() {
if (messanger == null) {
messanger = new Messenger(new SettingsProcessor());
}
return messanger.getBinder();
}
class SettingsProcessor extends Handler {
private static final int GET_SETTINGS_REQUEST = 1;
private static final int UPDATE_SETTINGS_RESPONSE = 2;
private static final String SETTINGS = "settings";
#Override
public void handleMessage(Message msg) {
String callingApp = MyApplication.getAppContext().getPackageManager().getNameForUid(Binder.getCallingUid());
Log.e("TEST", "handle message - package name: " + callingApp);
switch (msg.what) {
case GET_SETTINGS_REQUEST:
sendSettingsValue(msg);
break;
default:
super.handleMessage(msg);
}
}
private void sendSettingsValue(Message msg) {
try {
Message resp = Message.obtain(null, UPDATE_SETTINGS_RESPONSE);
Bundle bundle = new Bundle();
bundle.putBoolean(SETTINGS, MyApplication.isSettingsEnabled());
resp.setData(bundle);
msg.replyTo.send(resp);
} catch (RemoteException e) {
// ignore
}
}
}
}
Output on calling api:
02-01 15:21:03.138 7704-7704/my.service.package E/TEST: package intent: null
02-01 15:21:03.139 7704-7704/my.service.package E/TEST: onBind - package name: my.service.package
02-01 15:21:12.429 7704-7704/my.service.package E/TEST: handle message - package name: my.service.package

OK, I was able to solve this problem based on a given answer here. The answer given in the link obviously does not works, but you can get the app ID from the Handler used for the bound service.
class SettingsProcessor extends Handler {
#Override
public void handleMessage(Message msg) {
String callingApp = MyApplication.getAppContext().getPackageManager().getNameForUid(msg.sendingUid);
Log.e("TEST", "handle message - package name: " + callingApp);
}
}
Instead of Binder.getCallingUid(), I am using msg.sendingUid and it works fine for me.

Related

Google Cloud Messaging register method provides a null value

As recommended in the available documentation I decided to implement an automatic update whenever there is an update of the version of my application.
For doing that I have a service that is running in the background performing several operations appart from the GCM update. This service is calling a class that performs all operations related to GCM.
So, basically, this is the call to performed in the Service:
try {
PackageInfo info = manager.getPackageInfo(this.getPackageName(), 0);
currentVersion = info.versionCode;
} catch (NameNotFoundException e) {
//Handle exception
}
if (registeredVersion != currentVersion) {
Log.i(ApplicationData.APP_TAG, TAG + ": New version, updating");
GcmUpdater upGcm = new GcmUpdater(getApplicationContext());
Boolean update = upGcm.getAndUpdate();
//We update the current version
if (update) {
prefs.setAppPrevVersion(currentVersion);
} else {
Log.e(ApplicationData.APP_TAG, TAG + ": GCM not updated");
}
} else {
Log.i(ApplicationData.APP_TAG, TAG + ": Same version, no GCM needed");
}
Ok, I think the key point in the previous code is that I am initiating the class called GcmUpdater is initiated using the application context given by the service.
The constructor of my class GcmUpdater is the following:
public GcmUpdater(Context cont) {
context = cont;
TAG = getClass().getName();
prefs = new StorePreferences(context);
}
Nothing special, as you can see I am calling the method inside GcmUpdater called getAndUpdate(), this method is the following one
public Boolean getAndUpdate() {
String new_regid = giveRegId();
return updateGCM(new_regid);
}
Ok, the problem is coming now, is the public function giveRegId()
public String giveRegId() {
try{
return new RegisterGCM().execute().get();
}catch(Exception ex){
ex.printStackTrace();
return null;
}
}
Which calls to the asyncronous task RegisterGCM....
public class RegisterGCM extends AsyncTask<Void,Void,String>
{
#Override
protected String doInBackground(Void... arg0)
{
try {
if (gcm == null) {
gcm = GoogleCloudMessaging.getInstance(context);
}
regid = gcm.register(ApplicationData.SENDER_ID);
Log.i(ApplicationData.APP_TAG, TAG +":Device registered, registration ID=" + regid);
} catch (IOException ex) {
Log.e(ApplicationData.APP_TAG, TAG + ": " + ex.getMessage());
}
return regid;
}
protected void onPostExecute(Boolean result) {
return ;
}
}
The problem I am facing is that the variable regid obtained is null and according to similar problems like this one or this other one, I should include the ApplicationContext, however that is passed as parameter in the constructor.
Moreover, the class RegisterGCM is used by my main activity and works. So my guess has been always that the way to call to register the GCM code is the one that is creating the problem, but is not clear why.
What am I doing wrong? I have not been able to find any explication of this problem in google.
Your sender ID is equals to Google Console Project Id?

OnMessageReceived not called in WearableListenerService

I am working on android wear app using Eclipse IDE.I am using same package names for wear app and mobile app and i am packing wearable app manually according to google documentation.Everything is working fine.it is installed on Android wear emulator using usb debugging with phone.
My problem is when i am sending a message to wearable using following code
List<Node> nodeList=getNodes();
for(Node node : nodeList) {
Log.v(" ", "telling " + node.getId() );
PendingResult<MessageApi.SendMessageResult> result = Wearable.MessageApi.sendMessage(
mGoogleApiClient,
node.getId(),
START_ACTIVITY_PATH,
null
);
result.setResultCallback(new ResultCallback<MessageApi.SendMessageResult>() {
#Override
public void onResult(MessageApi.SendMessageResult sendMessageResult) {
Log.v(" ", "Phone: " + sendMessageResult.getStatus().getStatusMessage());
}
});
}
the OnPeerConnected method is running when devices are peered but OnMessageReceived never called in WearableListenerService.This is my WearableListenerService code:
public class DataLayerListenerService extends WearableListenerService {
private static final String TAG = "DataLayerSample";
private static final String START_ACTIVITY_PATH = "/start/MainActivity";
private static final String DATA_ITEM_RECEIVED_PATH = "/data-item-received";
private static final String LOG_TAG = "log";
#Override
public void onPeerConnected(Node peer) {
super.onPeerConnected(peer);
String id = peer.getId();
String name = peer.getDisplayName();
Log.d(LOG_TAG, "Connected peer name & ID: " + name + "|" + id);
}
#Override
public void onDataChanged(DataEventBuffer dataEvents) {
System.out.println("Recevive message3");
}
#Override
public void onMessageReceived(MessageEvent messageEvent) {
System.out.println("service watch message1");
if (messageEvent.getPath().equals(START_ACTIVITY_PATH)) {
System.out.println("service watch message2");
Intent startIntent = new Intent(this, MainActivity.class);
startIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(startIntent);
}
}
}
Also a warning message in Logcat always appears :
app does not match record's app key: AppKey[com.myapp,c3f31717fa35401056c20a2798907f1232efa75e] != AppKey[com.myapp,f36e726eefc7e528db26a1c25f6fbf2f93dacd70]
If app key for both apps should be same then how can i create same app key for both the apps.
Any help is highly appreciated,
Thanks.
The error message you have:
app does not match record's app key:
AppKey[com.myapp,c3f31717fa35401056c20a2798907f1232efa75e] !=
AppKey[com.myapp,f36e726eefc7e528db26a1c25f6fbf2f93dacd70]
Indicated that your apps are signed with the different keys.
Package names of phone and wearable apps are the same - that is good, but they also need to share the same signature. This is the reason why messages cannot be delivered - wearable apps are recognized as "part of the same app" based on the package name and signature.
Please make sure that you have both apps signed with the same key. If you are testing the autoinstallation feature please make sure to uninstall the debug version of wearable app from watch emulator.
I had the same error, my fault was that the "wear" module's package name was not the same as the app's.
BAD:
[module: app] es.voghdev.myapp
[module: wear] es.voghdev.myapp.wear
GOOD:
[module: app] es.voghdev.myapp
[module: wear] es.voghdev.myapp
Made me waste so much time!! >:-(
Use an asyntask to send messages as they will block the ui thread. Also you need to call the await method. To get the apps to have the same key, you need to use build variants with gradle.
public class SendMessageTask extends AsyncTask<Void, Void, Void> {
#Override
protected Void doInBackground(Void... voids) {
NodeApi.GetConnectedNodesResult nodes =
Wearable.NodeApi.getConnectedNodes(apiClient).await();
for (Node node : nodes.getNodes()) {
Wearable.MessageApi
.sendMessage(apiClient, node.getId(), "/start/MainActivity", null)
.await();
}
return null;
}
#Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
Toast.makeText(MainActivity.this, "Message Sent", Toast.LENGTH_SHORT).show();
}
}

One of my Application's view inside my second application in android

I got stuck in to the problem where I need to show my first application in to some area of second application's screen. Both codes are under my control. Can any one suggest me where should I proceed as I am not getting any clue about the situation.
if some one help me for the issue, it would be a great help for me.
Or
If I can open both of my applications using the multiscreen option available in S3.
Write a service on either of your application or a individual application. Have AIDL(Android Interface Definition Language) defined as IRemoteService.aidl, the below is my pseudo code or sample implementation. Using this approach you can start activity and handle events of another application through your application.
// IRemoteService.aidl
// Declare any non-default types here with import statements
/** Example service interface */
interface IAccountService {
String getLoggedInUserInfo(String appId);
void userLogin(String appId,ILoginCallback cb);
void signout(String appId);
}
interface ILoginCallback {
void loginSuccess(String userId);
void loginFailed();
}
In your service have some RemoteCallbacks
#Override
public IBinder onBind(Intent intent) {
final RemoteCallbackList<ILoginCallback> mCallbacks = new RemoteCallbackList<ILoginCallback>();
if(mCallbacks!=null){
int i = mCallbacks.beginBroadcast();
while(i>0){
i--;
try {
Log.e(TAG, "Callback ...");
mCallbacks.getBroadcastItem(i).loginSuccess(newUserId);
} catch (RemoteException e) {
// The RemoteCallbackList will take care of removing
// the dead object for us.
}
}
mCallbacks.finishBroadcast();
}
}
private final IAccountService.Stub mBinder = new IAccountService.Stub() {
#Override
public void userLogin(String appId,ILoginCallback cb) throws RemoteException {
String userId = Settings.getSettings().getUserId();
if(userId ==null||userId.length()==0){
mCallbacks.register(cb);
Intent intent = new Intent(getApplicationContext(), AccountLoginActivity.class);
intent.putExtra("deviceId", Settings.getSettings().getDeviceUniqueId());
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
}
}
You can find detailed AIDL examples in the below links.
http://owenhuangtw.pixnet.net/blog/post/23760257-android-aidl-(android-interface-definition-language)
http://www.app-solut.com/blog/2011/04/using-the-android-interface-definition-language-aidl-to-make-a-remote-procedure-call-rpc-in-android/
https://github.com/afollestad/aidl-example

Android WebView No permission to modify given thread, which permission should I declare?

crash log:
java.lang.SecurityException: No permission to modify given thread
android.os.Process.setThreadPriority(Native Method)
android.webkit.WebViewCore$WebCoreThread$1.handleMessage(WebViewCore.java:764)
android.os.Handler.dispatchMessage(Handler.java:99)
android.os.Looper.loop(Looper.java:137)
android.webkit.WebViewCore$WebCoreThread.run(WebViewCore.java:829)
java.lang.Thread.run(Thread.java:856)
which permission should I declare?
http://developer.android.com/reference/android/Manifest.permission.html
edit:
I found a similar problem in WebView java.lang.SecurityException: No permission to modify given thread
The Answer say "It's cyanogen's fault."
However, in the thread http://code.google.com/p/cyanogenmod/issues/detail?id=5656&thanks=5656&ts=1341224425,cm menbers seem to deny it's CM's bug
Above all, my question is:
How to fix it from my app?
which permission should I declare?
There is no relevant permission for this AFAIK. Unfortunately, the implementation of setThreadPriority() is in native code, which makes it difficult for me to figure out what is going on.
cm menbers seem to deny it's CM's bug
No, they do not. Nobody has posted evidence that it is a problem in CM9 or higher, which is why they marked the issue as stale. If you are seeing this on CM9 or higher, I suggest that you update the issue. If you are seeing this on standard Android, please create a sample project that can reproduce the error.
How to fix it from my app?
You don't, in all likelihood. You could try running some experiments on your WebView, to see if there is some specific content that triggers this exception, and try to modify or eliminate that content.
I get dozens of crash log caused by this exception from my app (which depends on webview heavily), involved ROM version are 4.0.4 and 4.0.3.
It seems that there is no normal way to fix it, so i tried following hacking approach.
code snipet on 4.0.4:
private static Handler sWebCoreHandler;
// Class for providing Handler creation inside the WebCore thread.
private static class WebCoreThread implements Runnable {
// Message id for initializing a new WebViewCore.
private static final int INITIALIZE = 0;
private static final int REDUCE_PRIORITY = 1;
private static final int RESUME_PRIORITY = 2;
public void run() {
Looper.prepare();
Assert.assertNull(sWebCoreHandler);
synchronized (WebViewCore.class) {
sWebCoreHandler = new Handler() {
#Override
public void handleMessage(Message msg) {
// ...
// Process.setPriority(...)
}
};
// ...
}
// ...
}
}
I think this exception is thrown from sWebCoreHandler.handleMessage(), if we can wrap try/catch on handleMessage(), the problem could be fixed.
Handler class has four members:
final MessageQueue mQueue;
final Looper mLooper;
final Callback mCallback;
IMessenger mMessenger;
mQueue is set as mLooper.mQueue, mCallback is null in sWebCoreHandler, so we just need to set mLooper and mMessenger with values in sWebCoreHandler.
static Handler sProxyHandler = null;
static void tryTweakWebCoreHandler() {
// 4.0.3/4.0.4 rom
if (Build.VERSION.SDK_INT == Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {
tweakWebCoreHandle();
}
}
static private void tweakWebCoreHandle() {
if (sProxyHandler != null)
return;
try {
Field f = Class.forName("android.webkit.WebViewCore").getDeclaredField("sWebCoreHandler");
f.setAccessible(true);
Object h = f.get(null);
Object mMessenger = null;
Method m = Handler.class.getDeclaredMethod("getIMessenger", (Class<?>[])null);
m.setAccessible(true);
mMessenger = m.invoke(h, (Object[])null);
sProxyHandler = new WebCoreProxyHandler((Handler)h);
if (mMessenger != null) {
Field f1 = Handler.class.getDeclaredField("mMessenger");
f1.setAccessible(true);
f1.set(sProxyHandler, mMessenger);
}
f.set(null, sProxyHandler);
// Log.w(TAG, "sWebCoreHandler: " + h);
} catch (Throwable e) {
Log.w(TAG, "exception: " + e);
}
if (sProxyHandler == null)
sProxyHandler = new Handler();
}
static class WebCoreProxyHandler extends Handler {
final Handler handler;
public WebCoreProxyHandler(Handler handler) {
super(handler.getLooper());
this.handler = handler;
}
public void handleMessage(Message msg) {
// Log.w("WebCoreProxyHandler", "handle msg: " + msg.what);
try {
handler.handleMessage(msg);
} catch (Throwable tr) {
Log.w("WebCoreProxyHandler", "exception: " + tr);
}
}
}
Remain problem is when to invoke tryTweakWebCoreHandler(). I tried to invoke it after a WebView instance is created and tested on some devices, WebCoreProxyHandler.handleMessage() can be called.
Note: i just made some simple test, i'm not sure this problem is resolved as the origin exception can not be reproduced reliably.
If you decide to try this approach, please do enough test.

get access to com.android.internal.telephony.Call

I need to get access to com.android.internal.telephony.Call.
doing so:
// Initialize the telephony framework
PhoneFactory.makeDefaultPhones (this);
// Get the default phone
Phone phone = PhoneFactory.getDefaultPhone ();
CallManager mCM = CallManager.getInstance ();
mCM.registerPhone (phone);
Call call = mCM.getFirstActiveBgCall();
but does not extend to initialize the framework.
Help me to initialize Call.
I need to read the state of the call like:
IDLE, ACTIVE, HOLDING, DIALING, ALERTING, INCOMING, WAITING, DISCONNECTED, DISCONNECTING.
You need to make use of PhoneStateListener
It will provide you the facility to have your application listen for different state of a phone call. You will need to put <uses-permission android:name="android.permission.READ_PHONE_STATE"/> in your manifest file
You can but there is a critical requirement: the application must be signed at system level, meaning you are the manufacturer.
Here is how you write a Service that will broadcast an intent for every change in the foreground call state.
/*
* This implementation uses the com.android.internal.telephony package: you have
* to extract the framework classes .jar file from the platform (or the
* emulator) to compile this code. Also, add the jar file to the external
* libraries in the Java Build Path/libraries of the android project. </p>
*
* The jar file must match the android version you are building the application
* for. Because this implementation is using the internal packages it cannot be
* guaranteed to operate on later versions of android.
*/
public class CallStateNotificationService extends Service {
private static final String LOG_TAG = CallStateNotificationService.class.getSimpleName();
private Handler mHandler = new Handler() {
#Override
public void handleMessage(Message msg) {
// TODO Auto-generated method stub
super.handleMessage(msg);
if (msg.what == 101) {
CallManager callManager = CallManager.getInstance();
Call.State state = callManager.getActiveFgCallState();
Intent intent = new Intent(PhoneIntents.ACTION_PRECISE_CALL_STATE);
intent.putExtra(PhoneIntents.PRECISE_CALL_STATE, state.name());
Context context = getApplicationContext();
context.sendBroadcast(intent);
}
}
};
#Override
public void onCreate() {
super.onCreate();
try {
CallManager callManager = CallManager.getInstance();
if (callManager != null) {
callManager.registerForPreciseCallStateChanged(mHandler, 101, null);
} else {
Log.w(LOG_TAG, "Can't resolve CallManager reference"); //$NON-NLS-1$
}
} catch (Exception e) {
e.printStackTrace();
}
}
#Override
public void onDestroy() {
super.onDestroy();
CallManager callManager = CallManager.getInstance();
if (callManager != null) {
callManager.unregisterForPreciseCallStateChanged(mHandler);
} else {
Log.w(LOG_TAG, "Can't resolve CallManager reference"); //$NON-NLS-1$
}
}
And here is the definition of the custom broadcasted intents.
/** Intent action and extra argument names for CallStateNotificationService */
public final class PhoneIntents {
public static final String ACTION_PRECISE_CALL_STATE = "com.myorg.myapp.CALL_STATE";
public static final String PRECISE_CALL_STATE = "precise_call_state";
}
To have this code compile and link, you of course need to either build the program as part of the android distribution itself or import the class-framework by a method explained elsewhere on the Internet.
All of this is currently in an app under production.

Categories

Resources