我在一个应用程序中有一个服务,我可以从不同的应用程序访问这个服务。当应用程序尝试绑定此服务时,我想知道哪个应用程序试图在 onBind 函数中绑定我的服务,但我无法在 onBind 函数中获取此应用程序的包名称或 UID。
是否可以获得试图在 onBind 函数中绑定我的服务的应用程序名称或 UID?
我在一个应用程序中有一个服务,我可以从不同的应用程序访问这个服务。当应用程序尝试绑定此服务时,我想知道哪个应用程序试图在 onBind 函数中绑定我的服务,但我无法在 onBind 函数中获取此应用程序的包名称或 UID。
是否可以获得试图在 onBind 函数中绑定我的服务的应用程序名称或 UID?
您可以使用以下内容来确定调用应用程序。
String callingApp = context.getPackageManager().getNameForUid(Binder.getCallingUid());
重要的是要注意JavaDocgetCallingUid()
说:
返回分配给向您发送当前正在处理的事务的进程的 Linux uid。此 uid 可与更高级别的系统服务一起使用,以确定其身份并检查权限。如果当前线程当前没有执行传入事务,则返回它自己的 uid。
你不能这样做。
onBind() 从 Android“生命周期管理器”(组成一个有用的名称)调用,并且只会为每个 Intent 调用一次(因此它可以了解应该为该 Intent 返回哪个 Binder)。
然后对您的服务的调用通过该 Binder 进行,您可以在其中任何一种方法中执行 Binder.getCallingUid()。
接受的答案不太正确!为什么?如果两个或多个应用程序使用相同android:sharedUserId
的,该方法Binder.getCallingUid()
将返回相同的uid并将getPackageManager().getNameForUid(uid)
返回相同的字符串,它看起来像:com.codezjx.demo:10058,但不是包名!
正确的方法是使用pid:
int pid = Binder.getCallingPid();
然后使用pid获取包名ActivityManager
,每个进程可以持有多个包,如下图:
private String[] getPackageNames(Context context, int pid) {
ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
List<RunningAppProcessInfo> infos = am.getRunningAppProcesses();
if (infos != null && infos.size() > 0) {
for(RunningAppProcessInfo info : infos) {
if(info.pid == pid) {
return info.pkgList;
}
}
}
return null;
}
警告:使用方法时Binder.getCallingPid()
,如果当前线程当前未执行传入事务,则返回其自己的 pid。这意味着您需要在 AIDL 公开的接口方法中调用此方法。
上述接受的答案对我不起作用。但一个小小的修改就奏效了。这非常适合基于Messenger的通信。
public class BoundService extends Service {
public static final int TEST = 100;
private final Messenger messenger = new Messenger(new MessageHandler());
class MessageHandler extends Handler {
@Override
public void handleMessage(Message msg) {
String callerId = getApplicationContext().getPackageManager().getNameForUid(msg.sendingUid);
Toast.makeText(getApplicationContext(), "Calling App: " + callerId, Toast.LENGTH_SHORT).show();
switch (msg.what) {
case TEST:
Log.e("BoundService", "Test message successfully received.")
break;
default:
super.handleMessage(msg);
}
}
}
@Override
public IBinder onBind(Intent intent) {
return messenger.getBinder();
}
}
从上面的答案,你只需要将 fromBinder.getCallingUid()
改为msg.sendingUid
我正在寻找如何LocationManagerService
限制访问,这是我发现的:
// android.location.ILocationService
Location getLastLocation(LocationRequest request, String packageName) throws RemoteException;
// com.android.server.LocationManagerService
public Location getLastLocation(LocationRequest r, String packageName) {
...
checkPackageName(packageName);
// From this point on we assume that the provided packageName is the real one
if (mBlacklist.isBlacklisted(packageName)) {
if (D) {
Log.d(TAG, "not returning last loc for blacklisted app: "
+ packageName);
}
return null;
}
...
}
...
private void checkPackageName(String packageName) {
if (packageName == null) {
throw new SecurityException("invalid package name: " + null);
}
int uid = Binder.getCallingUid();
String[] packages = mPackageManager.getPackagesForUid(uid);
if (packages == null) {
throw new SecurityException("invalid UID " + uid);
}
for (String pkg : packages) {
if (packageName.equals(pkg)) return;
}
throw new SecurityException("invalid package name: " + packageName);
}
我想,这是令人满意的,因为要让应用程序共享 uid,它们需要使用相同的密钥进行签名,因此可以同样受到信任
您现在可以使用getPackagesForUid
而不是getNameForUid
.
(getName
postPends uid,getPackages
没有)
class MyService : Service() {
override fun onBind(intent: Intent): IBinder {
return Messenger(IncomingHandler(this, packageManager).binder
}
internal class IncomingHandler(
private val context: Context,
private val packageManager: PackageManager
) : Handler() {
override fun handleMessage(msg: Message) {
val sendingUid = msg.sendingUid
val potentialCallingPackageNames = packageManager.getPackagesForUid(sendingUid)!!.toList()
Log.d("TUT", "One of these packages called you $potentialCallingPackageNames")
}
}
}
请记住,如果您希望系统应用程序调用您,那么它的 UID 将匹配许多其他系统应用程序,因此列表将有很多项目。如果它只是另一个应用程序呼叫您,那么列表可能会有 1 个项目。