mirror of
https://github.com/termux/termux-app
synced 2024-06-17 14:47:08 +00:00
Added|Fixed: Added the option to specify the binding priority for callback services, fixed callback services, fixed plugin UID accidentally being set to the PID.
This commit is contained in:
parent
f459ee481e
commit
b1fe382dff
|
@ -30,6 +30,7 @@
|
|||
<permission
|
||||
android:name="${TERMUX_PACKAGE_NAME}.permission.TERMUX_SIGNATURE"
|
||||
android:protectionLevel="signature" />
|
||||
<uses-permission android:name="${TERMUX_PACKAGE_NAME}.permission.TERMUX_SIGNATURE" />
|
||||
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
|
|
|
@ -14,7 +14,6 @@ import android.os.NetworkOnMainThreadException;
|
|||
import android.os.ParcelFileDescriptor;
|
||||
import android.os.Process;
|
||||
import android.os.RemoteException;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
@ -97,7 +96,8 @@ public class PluginService extends Service
|
|||
Map<Integer, NativeShell> tasks = Collections.synchronizedMap(new HashMap<>());
|
||||
|
||||
@NonNull IPluginCallback callback;
|
||||
int cachedCallbackVersion;
|
||||
final int cachedCallbackVersion;
|
||||
ServiceConnection con = null;
|
||||
|
||||
Plugin(int pid, int uid, @NonNull IPluginCallback callback) throws RemoteException {
|
||||
this.pid = pid;
|
||||
|
@ -106,6 +106,11 @@ public class PluginService extends Service
|
|||
callback.asBinder().linkToDeath(() -> mConnectedPlugins.remove(pid), 0); // remove self when the callback binder dies
|
||||
cachedCallbackVersion = callback.getCallbackVersion();
|
||||
}
|
||||
|
||||
Plugin(int pid, int uid, @NonNull IPluginCallback callback, ServiceConnection con) throws RemoteException {
|
||||
this(pid, uid, callback);
|
||||
this.con = con;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
|
@ -132,6 +137,13 @@ public class PluginService extends Service
|
|||
@Override
|
||||
public void onDestroy() {
|
||||
unbindService(mTermuxServiceConnection);
|
||||
for (Map.Entry<Integer, Plugin> e : mConnectedPlugins.entrySet()) {
|
||||
if (e.getValue().con != null) {
|
||||
try {
|
||||
unbindService(e.getValue().con);
|
||||
} catch (IllegalArgumentException ignored) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -149,7 +161,7 @@ public class PluginService extends Service
|
|||
* If not already, this creates an entry in the map of connected plugins for the current Binder client.
|
||||
*/
|
||||
private void addClient(@NonNull IPluginCallback callback) {
|
||||
int pid = Binder.getCallingPid(), uid = Binder.getCallingPid();
|
||||
int pid = Binder.getCallingPid(), uid = Binder.getCallingUid();
|
||||
if (pid == Process.myPid()) return; // no client connected
|
||||
if (mConnectedPlugins.get(pid) != null) return; // client already in list
|
||||
try {
|
||||
|
@ -157,6 +169,16 @@ public class PluginService extends Service
|
|||
} catch (RemoteException ignored) {} // RemoteException is thrown if the callback binder is already dead, plugin isn't added to the list
|
||||
}
|
||||
|
||||
/**
|
||||
* If not already, this creates an entry in the map of connected plugins for the current Binder client.
|
||||
*/
|
||||
private void addClientWithCallbackService(@NonNull IPluginCallback callback, int pid, int uid, @NonNull ServiceConnection con) {
|
||||
if (mConnectedPlugins.get(pid) != null) return; // client already in list
|
||||
try {
|
||||
mConnectedPlugins.put(pid, new Plugin(pid, uid, callback, con));
|
||||
} catch (RemoteException ignored) {} // RemoteException is thrown if the callback binder is already dead, plugin isn't added to the list
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private Plugin checkClient() throws IllegalStateException {
|
||||
Plugin p = mConnectedPlugins.get(Binder.getCallingPid());
|
||||
|
@ -212,69 +234,86 @@ public class PluginService extends Service
|
|||
public void setCallbackBinder(IPluginCallback callback) {
|
||||
externalAppsOrThrow();
|
||||
if (callback == null) throw new NullPointerException("Passed callback binder is null");
|
||||
if (mConnectedPlugins.get(Binder.getCallingPid()) != null) {
|
||||
throw new IllegalStateException("Callback binder already set (there is only one global connection to the plugin service, all new ones use the already initialized one)");
|
||||
}
|
||||
addClient(callback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCallbackService(String componentNameString) {
|
||||
public void setCallbackService(String componentNameString, int priority) {
|
||||
externalAppsOrThrow();
|
||||
if (componentNameString == null) throw new NullPointerException("Passed componentName is null");
|
||||
|
||||
String callerPackageName = BinderUtils.getCallerPackageNameOrNull(PluginService.this);
|
||||
if (callerPackageName == null) throw new NullPointerException("Caller package is null");
|
||||
|
||||
if (priority != PRIORITY_MIN &&
|
||||
priority != PRIORITY_NORMAL &&
|
||||
priority != PRIORITY_IMPORTANT &&
|
||||
priority != PRIORITY_MAX)
|
||||
throw new IllegalArgumentException("Invalid priority parameter");
|
||||
|
||||
if (mConnectedPlugins.get(Binder.getCallingPid()) != null) {
|
||||
throw new IllegalStateException("Callback binder already set (there is only one global connection to the plugin service, all new ones use the already initialized one)");
|
||||
}
|
||||
|
||||
ComponentName componentName = ComponentName.createRelative(callerPackageName, componentNameString);
|
||||
Intent callbackStartIntent = new Intent();
|
||||
callbackStartIntent.setComponent(componentName);
|
||||
|
||||
|
||||
final boolean[] bindingFinished = {false};
|
||||
final IBinder[] callbackBinder = new IBinder[] {null};
|
||||
|
||||
final int pid = Binder.getCallingPid();
|
||||
final int uid = Binder.getCallingUid();
|
||||
|
||||
ServiceConnection con = new ServiceConnection()
|
||||
{
|
||||
@Override
|
||||
public void onServiceConnected(ComponentName name, IBinder service) {
|
||||
callbackBinder[0] = service;
|
||||
bindingFinished[0] = true;
|
||||
synchronized (callbackBinder) {
|
||||
callbackBinder.notifyAll();
|
||||
}
|
||||
addClientWithCallbackService(IPluginCallback.Stub.asInterface(service), pid, uid, this);
|
||||
}
|
||||
@Override
|
||||
public void onServiceDisconnected(ComponentName name) {
|
||||
unbindService(this);
|
||||
try {
|
||||
PluginService.this.unbindService(this);
|
||||
} catch (IllegalArgumentException ignored) {}
|
||||
}
|
||||
@Override
|
||||
public void onBindingDied(ComponentName name) {
|
||||
unbindService(this);
|
||||
try {
|
||||
PluginService.this.unbindService(this);
|
||||
} catch (IllegalArgumentException ignored) {}
|
||||
}
|
||||
@Override
|
||||
public void onNullBinding(ComponentName name) {
|
||||
bindingFinished[0] = true;
|
||||
synchronized (callbackBinder) {
|
||||
callbackBinder.notifyAll();
|
||||
}
|
||||
unbindService(this);
|
||||
Logger.logDebug("Null binding for callback service");
|
||||
try {
|
||||
PluginService.this.unbindService(this);
|
||||
} catch (IllegalArgumentException ignored) {}
|
||||
}
|
||||
};
|
||||
|
||||
PluginService.this.bindService(callbackStartIntent, con, Context.BIND_ALLOW_OOM_MANAGEMENT);
|
||||
|
||||
while (! bindingFinished[0]) {
|
||||
try {
|
||||
synchronized (callbackBinder) {
|
||||
callbackBinder.wait();
|
||||
}
|
||||
try {
|
||||
boolean ret;
|
||||
switch (priority) {
|
||||
case PRIORITY_MIN:
|
||||
ret = PluginService.this.bindService(callbackStartIntent, con, Context.BIND_WAIVE_PRIORITY | Context.BIND_AUTO_CREATE);
|
||||
break;
|
||||
case PRIORITY_IMPORTANT:
|
||||
ret = PluginService.this.bindService(callbackStartIntent, con, Context.BIND_IMPORTANT | Context.BIND_AUTO_CREATE);
|
||||
break;
|
||||
case PRIORITY_MAX:
|
||||
ret = PluginService.this.bindService(callbackStartIntent, con, Context.BIND_ABOVE_CLIENT | Context.BIND_AUTO_CREATE);
|
||||
break;
|
||||
case PRIORITY_NORMAL:
|
||||
default:
|
||||
ret = PluginService.this.bindService(callbackStartIntent, con, Context.BIND_AUTO_CREATE);
|
||||
}
|
||||
catch (InterruptedException ignored) {}
|
||||
if (! ret) {
|
||||
throw new IllegalStateException("Cannot start service");
|
||||
}
|
||||
} catch (SecurityException e) {
|
||||
throw new IllegalArgumentException("Service not found or requires permissions");
|
||||
}
|
||||
|
||||
if (callbackBinder[0] == null) {
|
||||
throw new IllegalArgumentException("Could not bind callback service: "+componentNameString);
|
||||
}
|
||||
|
||||
addClient(IPluginCallback.Stub.asInterface(callbackBinder[0]));
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -27,6 +27,7 @@ interface IPluginCallback {
|
|||
|
||||
/**
|
||||
* This gets called when a connection is made on a socket created with {@link com.termux.plugin_aidl.IPluginService#listenOnSocketFile}.
|
||||
* <br>Added in version 1.
|
||||
*
|
||||
* @param sockname The name of socket file the connection was made on (the relative path to the plugin directory).
|
||||
* @param connection The connection file descriptor.
|
||||
|
@ -36,6 +37,7 @@ interface IPluginCallback {
|
|||
|
||||
/**
|
||||
* Gets called when a started Task exits.
|
||||
* <br>Added in version 1.
|
||||
*
|
||||
* @param pid The pid of the task that exited.
|
||||
* @param code The exit code of the Task. Negative values indicate the Task was killed by a signal. The signal number is then {@code -code}.
|
||||
|
|
|
@ -24,6 +24,30 @@ import com.termux.plugin_aidl.Task;
|
|||
*/
|
||||
interface IPluginService {
|
||||
|
||||
/**
|
||||
* Binds the service with @{link android.content.Context#BIND_WAIVE_PRIORITY}, meaning the plugins needs other components for the system not to kill it.
|
||||
*/
|
||||
const int PRIORITY_MIN = 0;
|
||||
|
||||
/**
|
||||
* Uses no flags to bind the service.
|
||||
*/
|
||||
const int PRIORITY_NORMAL = 1;
|
||||
|
||||
/**
|
||||
* Binds the service with @{link android.content.Context#BIND_IMPORTANT}, which should give the plugin the same priority as Termux in terms of OOM killing.
|
||||
*/
|
||||
const int PRIORITY_IMPORTANT = 2;
|
||||
|
||||
/**
|
||||
* Binds the service with @{link android.content.Context#BIND_ABOVE_CLIENT}, which would make Termux get killed first on OOM.
|
||||
* This should be used if you really don't want the plugin killed while a program uses it, and your plugin has a small memory footprint.
|
||||
* Another solution for that is to make your own foreground service, but the undismissable notification could be annoying for users, so this is given as an option.
|
||||
*/
|
||||
const int PRIORITY_MAX = 3;
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* This or {@link com.termux.plugin_aidl.IPluginService#setCallbackService} has to be called before any other method.
|
||||
|
@ -33,12 +57,15 @@ interface IPluginService {
|
|||
|
||||
/**
|
||||
* This or {@link com.termux.plugin_aidl.IPluginService#setCallbackBinder} has to be called before any other method.
|
||||
* It initialized the internal representation of the connected plugin and sets the callback binder to the binder returned by the bound service.
|
||||
* It initializes the internal representation of the connected plugin and sets the callback binder to the binder returned by the bound service.
|
||||
* You can call other methods shortly after getCallbackVersion has been called in the supplied service.
|
||||
* If it's too early the methods will throw an {@link IllegalStateException} which can be caught.
|
||||
*
|
||||
* @param componentName This is the relative part of a component name string.
|
||||
* The package name is always taken from the calling binder package for security reasons.
|
||||
* @param priority This is the priority Termux should bind the service with. See the PRIORITY_* constants for more info.
|
||||
*/
|
||||
void setCallbackService(String componentName) = 2;
|
||||
void setCallbackService(String componentName, int priority) = 2;
|
||||
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in New Issue
Block a user