/*
* CatSaver
* Copyright (C) 2015 HiHex Ltd.
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package hihex.cs;
import com.google.common.base.Charsets;
import com.google.common.io.ByteStreams;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
/**
* Utility class to try to get root/shell permission to grant this package the READ_LOGS permission.
*/
public final class GrantPermission {
private static final String GRANT_COMMAND = "pm grant hihex.cs android.permission.READ_LOGS";
private static final String ADB_CONNECT_MESSAGE = "CNXN\0\0\0\1\0\u0010\0\0\7\0\0\u00002\2\0\0\u00bc\u00b1\u00a7\u00b1host::\0";
private static final String ADB_GRANT_MESSAGE = "OPEN\1\0\0\0\0\0\0\u00005\0\0\0\u00fb\u0012\0\0\u00b0\u00af\u00ba\u00b1shell:" + GRANT_COMMAND + '\0';
/**
* Try to grant this package the READ_LOGS permission using various (legitimate) methods. There is no guarantee this
* will succeed — you still have to use {@link android.content.Context#checkCallingOrSelfPermission(String)
* checkCallingOrSelfPermission()} to ensure you have got the correct permissions.
* <p/>
* <p>This method will run external programs and access sockets, so it must not be called in the main thread.</p>
*/
public static void tryGrantPermission() {
tryGrantPermissionViaSu();
tryGrantPermissionViaAdb();
}
private static void tryGrantPermissionViaSu() {
final Runtime runtime = Runtime.getRuntime();
final String[] args = {"su", "-c", GRANT_COMMAND};
try {
final Process process = runtime.exec(args);
process.waitFor();
} catch (final IOException e) {
CsLog.e("Failed to grant permission using `su`: " + e);
} catch (final InterruptedException e) {
CsLog.e("Interrupted while granting permission with `su`");
}
}
private static void tryGrantPermissionViaAdb() {
final Socket socket = new Socket();
try {
final SocketAddress address = new InetSocketAddress(InetAddress.getLocalHost(), 5555);
socket.setSoTimeout(1000);
socket.connect(address);
final byte[] readHeader = new byte[24];
final OutputStream outputStream = socket.getOutputStream();
final InputStream inputStream = socket.getInputStream();
outputStream.write(ADB_CONNECT_MESSAGE.getBytes(Charsets.ISO_8859_1));
readAdbPacket(inputStream, readHeader);
outputStream.write(ADB_GRANT_MESSAGE.getBytes(Charsets.ISO_8859_1));
readAdbPacket(inputStream, readHeader);
readAdbPacket(inputStream, readHeader);
} catch (final IOException e) {
CsLog.e("Failed to grant permission using `adb`: " + e);
} finally {
try {
socket.close();
} catch (final IOException e) {
// ignore
}
}
}
private static void readAdbPacket(final InputStream stream, final byte[] header) throws IOException {
ByteStreams.readFully(stream, header);
long length = ByteBuffer.wrap(header).order(ByteOrder.LITTLE_ENDIAN).get(12);
ByteStreams.skipFully(stream, length);
}
}