...

воскресенье, 26 января 2014 г.

Модификация стоковых прошивок для Android. Часть 4

Здравствуй Хабр!

В прошлой статье мы обсудили некоторые нюансы касательно интерфейса, а сегодня попробуем разобрать детально каждый случай в отдельности.


На прошлых скриншотах были следующие меню в моем самодельном твикере и вызвало множество приватных вопросов о реализации.


Предпочтительный слот

Выберите SIM карту на которой использовать передачу данных

Уведомление о соединении

Запретить оповещение об интернет подключении

Автоматическая запись звонков

Все звонки будут записаны стандартным диктофоном согласно его настройкам

Запретить энергосбережение

Запретить иконку энергосбережения в слайдере и статус баре

Запретить выключатели

Отключение в слайдере статус бара



Предпочтительный слот




Так как я являюсь ярым поклонником двухсимочных телефонов, данная функция мне нужна для того, чтобы иметь возможность использовать интернет от любого из операторов, где есть покрытие. 3G/GPRS/EDGE покрытие у всех разное, а необходимость быть действительно мобильным — для меня задача первостепенная. По умолчанию интернет работает на первой основной сим карте, но в некоторых местах оператор не имеет 3G и предоставляет слабую пропускную способность, урезая EDGE тайм слоты на канале передачи данных, соответственно передача идет по GPRS. Имя такой твикер я могу легко переключиться на второго оператора и иметь подключение по крайней мере под EDGE.

Модифицировать прошивку для этого не обязательно, а достаточно вызвать диалог и указать что вам необходимо. Сразу отмечу, что данный код применим к телефонам HTC и был написан согласно библиотеке android.net.HtcIfConnectivityManager.


HtcIfConnectivityManager


String slot1 = Settings.System.getString(getContentResolver(), "slot_1_user_text") != null ? Settings.System.getString(getContentResolver(), "slot_1_user_text") : "SIM 1";
String slot2 = Settings.System.getString(getContentResolver(), "slot_2_user_text") != null ? Settings.System.getString(getContentResolver(), "slot_2_user_text") : "SIM 2";

CharSequence[] slots = { slot1, slot2 };

new HtcAlertDialog.Builder(this).setTitle(R.string.type_title).setSingleChoiceItems(slots, -1, new DialogInterface.OnClickListener()
{
@Override
public void onClick(DialogInterface dialog, int which)
{
try
{
HtcIfConnectivityManager localHtcIfConnectivityManager = (HtcIfConnectivityManager) main.this.getApplicationContext().getSystemService("connectivity");
Integer type = 1;
switch (which)
{
default:
case 0:
type = 1;
break;
case 1:
type = 5;

}
localHtcIfConnectivityManager.setMobileDataPhoneType(type);
dialog.dismiss();
return;
}
catch (Exception localException1)
{
Log.d("Falseclock", "type change:" + localException1);
}
}
}).show();







Уведомление о соединении




В прошлой статье я писал о ненужном уведомлении, что мой телефон в данный момент использует передачу данных и показывает какой APN используется. Честно говоря, мне это не то что нужно, а раздражало, что и послужило поводом отключения данной функции. Польностью вырезать из прошивки я не стал, так как публикую свои работы для общего пользования, а при создании модифицированных прошивок хорошим тоном считается оставлять конечному пользователю выбирать что ему нравится, а что нет.

Осталось только найти в каком месте данный функционал срабатывает. Надо отдать должное, программисты HTC хорошо оптимизировали код, его приятно читать и легко находить нужное место. У ООП есть конечно и свои минусы, так как порой необходимый фрагмент кода нужно искать по целой цепочке методов. Еще одно преимущество, HTC Sense создан на шаблонах, которые по прохождению кода собираются как конструктор Lego, в оконцовке превращаясь в полноценный графический интерфейс. В стандартной документации исходного кода Android предлагается для каждого вызова (intent или dialog) рисовать отдельный шаблон (layout) и первое время искать приходилось очень долго, так как я искал интерфейс оболочки в самой XML разметке, а не в коде программы.


И так, в 4-ом Аднроиде есть замечательная функция, которая позволят узнать кто родитель уведомления. Достаточно долго нажать на уведомление и появится меню, в котором можно посмотреть приложение, которое является инициатором. В моем случае оказалось, что это приложение Телефон (Phone.apk).


Потрошим приложение



Распаковываем и декомпилируем приложение с помощью APK-Multi-Tool. Для этого предварительно надо скачать, установить и настроить его. Все описано в документации.

1. Кладем Phone.apk в папку place-apk-here-for-modding

2. Открываем любим архиватором и удаляем от туда файл classes.dex. Это ускорит работу и избавит вас от ошибок декомпилятора.

3. Запускаем скрипт Script.bat и выбираем 9-ый пункт Decompile apk. Нам нужно распаковать приложение и покопаться в файлах res/values. После распаковки исходники будут лежать в папке .\projects\Phone.apk\
Поиски кода



1. Так как у меня интерфейс русский, то мне нужна папка с русскими словами .\res\values-ru.

2. На скриншоте из прошлой статьи видим, что у нас есть слово «Подключено» и оно явно находится в нашей локализации.

3. Ищем по всем файлам наше слово… и не находим :-(

4. У нас есть еще иконка в виде двух стрелок, поищем ее. Идем в папку \projects\Phone.apk\res\drawable-hdpi и видим ее stat_sys_apn.png.

5. Ищем идентификатор картинки по ее названию.

TOTAL: 2 matches in 2 files (13 other files without matches are not listed)
1 match in S:\dev\Android\APK-Multi-Tool\projects\Phone.apk\res\values\drawables.xml
49 <item type="drawable" name="stat_sys_apn">@drawable/zero_dummy_asset</item>
1 match in S:\dev\Android\APK-Multi-Tool\projects\Phone.apk\res\values\public.xml
60 <public type="drawable" name="stat_sys_apn" id="0x7f02007f" />




6. Мы нашли шестнадцатиричный ID картинки 0x7f02007f, что в десятичном у нас 2130837631 (переводится в виндовом калькуляторе).

7. Теперь у нас есть два пути:

а) взять classes.dex, сконвертировать его в jar и открыть в gd-gui;

b) воспользоваться baksmali.jar и распотрошить Dalvik код (описывалось в первой части статей).

Я предпочитаю первый вариант, так как читать удобней (описывалось в первой статье, я главе «Распаковка и анализ оригинального файла»).

8. Открыв сконвертированный classes.dex в gd-gui, сохраним наш исходный код.

9. Сделаем поиск 2130837631 в наших исходниках:

TOTAL: 3 matches in 2 files (326 other files without matches are not listed)
2 matches in D:\Desktop\classes_dex2jar.src\com\android\phone\NotificationMgr.java
1237 HtcWrapNotification localHtcWrapNotification = new HtcWrapNotification(this.mContext, 2130837631, null, System.currentTimeMillis(), paramString, this.mContext.getString(2131624179), localIntent);
1282 HtcWrapNotification localHtcWrapNotification = new HtcWrapNotification(this.mContext, 2130837631, null, System.currentTimeMillis(), paramString, this.mContext.getString(2131624179), localIntent);
1 match in D:\Desktop\classes_dex2jar.src\com\android\phone\R.java
834 public static final int stat_sys_apn = 2130837631;




10. там же в gd-gui идем смотреть что это за код.

showMobileDataConnected


void showMobileDataConnected(String paramString)
{
if (DBG)
log("showMobileDataConnected()...");
Intent localIntent = new Intent("android.intent.action.MAIN");
if (PhoneApp.MODE_DUAL)
if (PhoneUtils.getMobileDataPhoneType() == 1)
localIntent.setComponent(new ComponentName("com.android.settings", "com.android.settings.ApnSettings"));
while (true)
{
HtcWrapNotification localHtcWrapNotification = new HtcWrapNotification(this.mContext, 2130837631, null, System.currentTimeMillis(), paramString, this.mContext.getString(2131624179), localIntent);
localHtcWrapNotification.flags = (0x2 | localHtcWrapNotification.flags);
this.mNotificationManager.notify(12, localHtcWrapNotification);
return;
localIntent.setComponent(new ComponentName("com.android.settings", "com.android.settings.CdmaApnSettings"));
continue;
localIntent.setComponent(new ComponentName("com.android.settings", "com.android.settings.ApnSettings"));
}
}

void showMobileDataConnected(String paramString, int paramInt)
{
if (DBG)
log("showMobileDataConnected---->>phoneType=" + paramInt + ", APN Name= " + paramString);
String str = "";
int i = -1;
Intent localIntent = new Intent("android.intent.action.MAIN");
if (paramInt == 2)
{
str = "com.android.settings.CdmaApnSettings";
i = 13;
}
while (true)
{
VLog.logd("NotificationMgr", "notificationId = " + i);
if (i != -1)
break;
VLog.logd("NotificationMgr", "notificationId is wrong!");
return;
if (paramInt == 1)
{
str = "com.android.settings.ApnSettings";
i = 14;
localIntent.putExtra("phone_type", paramInt);
if (PhoneApp.MODE_CG)
localIntent.putExtra("isSettings", 1);
}
else if (paramInt == 5)
{
str = "com.android.settings.ApnSettings";
i = 15;
localIntent.putExtra("phone_type", paramInt);
}
}
localIntent.setComponent(new ComponentName("com.android.settings", str));
HtcWrapNotification localHtcWrapNotification = new HtcWrapNotification(this.mContext, 2130837631, null, System.currentTimeMillis(), paramString, this.mContext.getString(2131624179), localIntent);
localHtcWrapNotification.flags = (0x2 | localHtcWrapNotification.flags);
localHtcWrapNotification.contentIntent = PendingIntent.getActivity(this.mContext, paramInt, localIntent, 134217728);
this.mNotificationManager.notify(i, localHtcWrapNotification);
}







11. Так как это просто метод, то значит он от куда-то вызывается. Давайте поищем.

TOTAL: 9 matches in 2 files (326 other files without matches are not listed)
4 matches in D:\Desktop\classes_dex2jar.src\com\android\phone\NotificationMgr.java
1227 void showMobileDataConnected(String paramString)
1230 log("showMobileDataConnected()...");
1247 void showMobileDataConnected(String paramString, int paramInt)
1250 log("showMobileDataConnected---->>phoneType=" + paramInt + ", APN Name= " + paramString);
5 matches in D:\Desktop\classes_dex2jar.src\com\android\phone\PhoneApp.java
914 NotificationMgr.getDefault().showMobileDataConnected(str4, i3);
917 NotificationMgr.getDefault().showMobileDataConnected(str4);
920 NotificationMgr.getDefault().showMobileDataConnected(str3);
5407 NotificationMgr.getDefault().showMobileDataConnected(PhoneApp.APNQueryThread.this.apnCarrier, PhoneApp.APNQueryThread.this.phoneType);
5412 NotificationMgr.getDefault().showMobileDataConnected(PhoneApp.APNQueryThread.this.apnCarrier);




12. Открываем в jd-gui файл com\android\phone\PhoneApp.java и понимаем что вызов у нас срабатывает в следующем блоке

FEATURE_APN_CONNECTION_NOTIFICATION


if (HtcFeatureList.FEATURE_APN_CONNECTION_NOTIFICATION)
{
if (str4 == null)
{
String str5 = "apn = '" + str3 + "' AND current IS NOT NULL";
Uri localUri = Telephony.Carriers.CONTENT_URI;
if (PhoneApp.MODE_DUAL)
{
if (TextUtils.isEmpty(str3))
{
VLog.logd("PhoneApp", "APN name is null!");
if (i3 == 2)
{
PhoneApp.access$3302(PhoneApp.this, false);
return;
}
if (i3 == 1)
{
PhoneApp.access$3402(PhoneApp.this, false);
return;
}
if (i3 != 5)
continue;
PhoneApp.access$3502(PhoneApp.this, false);
return;
}
VLog.logd("PhoneApp", "phone type = " + i3);
if (i3 != 2)
break label3803;
localUri = HtcWrapTelephony.CdmaCarriers.CONTENT_URI;
}
while (true)
{
PhoneApp.this.log("EVENT_MOBILE_DATA_CONNECTED, start APNQueryThread for APN query.");
new PhoneApp.APNQueryThread(PhoneApp.this, localUri, i3, str5, str3, str4).startQuery();
return;
label3803: if (i3 == 1)
localUri = HtcWrapTelephony.GsmCarriers.CONTENT_URI;
else if (i3 == 5)
localUri = HtcWrapTelephony.SubGsmCarriers.CONTENT_URI;
}
}
if (PhoneApp.MODE_DUAL)
{
NotificationMgr.getDefault().showMobileDataConnected(str4, i3);
return;
}
NotificationMgr.getDefault().showMobileDataConnected(str4);
return;
}







Модификация кода



Мы конечно можем пересетить переменную HtcFeatureList.FEATURE_APN_CONNECTION_NOTIFICATION, но как уже я говорил, это является дурным тоном жестко избавляться от кода, если вы публикуете прошивки и правильней будет сделать возможность выбора для пользователя. Разумеется, если вы делаете для себя и четко уверены, что вам это не нужно, можно вырезать радикально, но я все же не советую.

1. Так как у меня есть свой твикер, который хранит настройки в системной области (об этом в будущей статьей), нам нужно в начале этого блока сделать проверку что-то вроде:

if (HtcFeatureList.FEATURE_APN_CONNECTION_NOTIFICATION)
{
if (Settings.System.getInt(PhoneApp.this.phone.getContext().getContentResolver(), "tweaks_disableConnectionNotification", 0) != 0)
{
// основной код программы
}
}


Почему именно такой код? Я его просто подсмотрел несколькими строками выше:

if ((PhoneApp.this.phone.getPhoneType() != 2) && (HtcFeatureList.FEATURE_THIS_IS_WORLD_PHONE != true))
continue;
int i9 = 1;
int i10 = Settings.Secure.getInt(PhoneApp.this.phone.getContext().getContentResolver(), "preferred_tty_mode", 0);




нам же нужно всего-то посмотреть значение настройки с другой переменной.

2. Все, мы нашли что нам нужно и теперь готовы писать свой патчик. Даем команду java -Xmx512m -jar baksmali.jar -a <API LEVEL> -d <FRAMEWORK DIR> -o Phone -x Phone.apk

<API LEVEL> — это API вашей версии Android. Для JB — это 16

<FRAMEWORK DIR> — папка, где находятся все фреймворки прошивки.


В моем случае это была команда

java -Xmx512m -jar baksmali.jar -a 16 -d S:\dev\Android\Android-Kitchen\WORKING_JB_15\system\framework -o Phone -x Phone.apk

3. В нашей вновь созданной папке появилась папка Phone, а в ней наши файлы с Dalvik кодом.

4. Отыскиваем файл по пути \\com\android\phone\PhoneApp.java и смотрим код:



.line 1841
.local v7, phoneType:I
sget-boolean v4, Lcom/android/phone/HtcFeatureList;->FEATURE_APN_CONNECTION_NOTIFICATION:Z

if-eqz v4, :cond_c9c




5. Теперь после этой строки нам надо вставить нашу собственную проверку. Я нашел аналогичный код где проверяется настройка preferred_tty_mode. Нам ничего не стоит его взять и скопировать себе, поменяв название настройки и не беря первые две служебные строки

preferred_tty_mode


.line 1379
.local v43, setupTtyTakeAction:Z

move-object/from16 v0, p0

iget-object v4, v0, Lcom/android/phone/PhoneApp$3;->this$0:Lcom/android/phone/PhoneApp;

iget-object v4, v4, Lcom/android/phone/PhoneApp;->phone:Lcom/android/internal/telephony/Phone;

invoke-interface {v4}, Lcom/android/internal/telephony/Phone;->getContext()Landroid/content/Context;

move-result-object v4

invoke-virtual {v4}, Landroid/content/Context;->getContentResolver()Landroid/content/ContentResolver;

move-result-object v4

const-string v5, "preferred_tty_mode"

const/16 v62, 0x0

move/from16 v0, v62

invoke-static {v4, v5, v0}, Landroid/provider/Settings$Secure;->getInt(Landroid/content/ContentResolver;Ljava/lang/String;I)I

move-result v58







и в итоге получается
наш собственный модифицированный код


.line 1841
.local v7, phoneType:I
sget-boolean v4, Lcom/android/phone/HtcFeatureList;->FEATURE_APN_CONNECTION_NOTIFICATION:Z

if-eqz v4, :cond_c9c

move-object/from16 v0, p0

iget-object v4, v0, Lcom/android/phone/PhoneApp$3;->this$0:Lcom/android/phone/PhoneApp;

iget-object v4, v4, Lcom/android/phone/PhoneApp;->phone:Lcom/android/internal/telephony/Phone;

invoke-interface {v4}, Lcom/android/internal/telephony/Phone;->getContext()Landroid/content/Context;

move-result-object v4

invoke-virtual {v4}, Landroid/content/Context;->getContentResolver()Landroid/content/ContentResolver;

move-result-object v4

const-string v5, "tweaks_disableConnectionNotification"

const/16 v62, 0x0

move/from16 v0, v62

invoke-static {v4, v5, v0}, Landroid/provider/Settings$System;->getInt(Landroid/content/ContentResolver;Ljava/lang/String;I)I

move-result v58

// - выйти из блока







6. Теперь нам надо сделать проверку переменной v58 и в случае не соответствия выйти из условия. Только куда нам выходить? Покопавшись в исходном коде и разобрав алгоритм, я понял, что нам надо просто напросто уйти из метода возвратив void

который находится на строке 2327


# virtual methods
.method public handleMessage(Landroid/os/Message;)V
.registers 68
.parameter "msg"

.prologue
.line 1084
move-object/from16 v0, p1

iget v4, v0, Landroid/os/Message;->what:I

sparse-switch v4, :sswitch_data_16e6

.line 2327
:cond_7
:goto_7
:sswitch_7
return-void







7. Добавляем условие

if-nez v58, :cond_7


в наш модифицированный код и получаем

готовый патчик


.line 1841
.local v7, phoneType:I
sget-boolean v4, Lcom/android/phone/HtcFeatureList;->FEATURE_APN_CONNECTION_NOTIFICATION:Z

if-eqz v4, :cond_c9c

#---------------------------------------
# начало вживленного кода

move-object/from16 v0, p0

iget-object v4, v0, Lcom/android/phone/PhoneApp$3;->this$0:Lcom/android/phone/PhoneApp;

iget-object v4, v4, Lcom/android/phone/PhoneApp;->phone:Lcom/android/internal/telephony/Phone;

invoke-interface {v4}, Lcom/android/internal/telephony/Phone;->getContext()Landroid/content/Context;

move-result-object v4

invoke-virtual {v4}, Landroid/content/Context;->getContentResolver()Landroid/content/ContentResolver;

move-result-object v4

const-string v5, "tweaks_disableConnectionNotification"

const/16 v62, 0x0

move/from16 v0, v62

invoke-static {v4, v5, v0}, Landroid/provider/Settings$System;->getInt(Landroid/content/ContentResolver;Ljava/lang/String;I)I

move-result v58

if-nez v58, :cond_7

#---------------------------------------
# конец вживленного кода

.line 1844
if-nez v10, :cond_c86

.line 1845
new-instance v4, Ljava/lang/StringBuilder;







8. Даем команду java -Xmx512m -jar smali.jar -a 16 Phone -o classes.dex

9. В нашей папочке появляется файлик classes.dex

10. Снова открываем Phone.apk файл архиватором и заменяем в нем существующий classes.dex на наш только что созданный.

11. Все, наш Phone.apk содержит модифицированный программный код.

Автоматическая запись звонков




Реализацию данного твика я описал во второй части статей. Только там я покаывал код без использования твикера, так что выкладываю полную версию
onCallConnected


.method private onCallConnected(Landroid/os/AsyncResult;)V
.registers 8
.parameter "r"

.prologue

#---------------------------------------
# начало вживленного кода

iget-object v5, p0, Lcom/android/phone/CallNotifier;->mContext:Landroid/content/Context;

invoke-virtual {v5}, Landroid/content/Context;->getContentResolver()Landroid/content/ContentResolver;

move-result-object v5

const/4 v4, 0x0

const-string v3, "tweaks_enableAutoRecording"

invoke-static {v5, v3, v4}, Landroid/provider/Settings$System;->getInt(Landroid/content/ContentResolver;Ljava/lang/String;I)I

move-result v3

if-eq v3, v4, :cond_27

const-string v3, "Falseclocks: recording tweak is enabled"

invoke-direct {p0, v3}, Lcom/android/phone/CallNotifier;->log(Ljava/lang/String;)V

invoke-static {}, Lcom/android/phone/util/VoiceRecorderHelper;->getInstance()Lcom/android/phone/util/VoiceRecorderHelper;

move-result-object v3

invoke-virtual/range {v3 .. v3}, Lcom/android/phone/util/VoiceRecorderHelper;->isRecording()Z

move-result v4

const/4 v5, 0x0

if-ne v5, v4, :cond_27

invoke-virtual/range {v3 .. v3}, Lcom/android/phone/util/VoiceRecorderHelper;->start()Z

const-string v3, "Falseclock: automatic recording started"

invoke-direct {p0, v3}, Lcom/android/phone/CallNotifier;->log(Ljava/lang/String;)V

:cond_27

#---------------------------------------
# конец вживленного кода

const/4 v5, 0x0

.line 2302
iget-object v0, p1, Landroid/os/AsyncResult;->result:Ljava/lang/Object;

check-cast v0, Lcom/android/internal/telephony/Connection;





и

onDisconnect


.method private onDisconnect(Landroid/os/AsyncResult;)V
.registers 41
.parameter "r"

.prologue
#---------------------------------------
# начало вживленного кода
move-object/from16 v0, p0

iget-object v0, v0, Lcom/android/phone/CallNotifier;->mApplication:Lcom/android/phone/PhoneApp;

move-object/from16 v34, v0

invoke-virtual/range {v34 .. v34}, Lcom/android/phone/PhoneApp;->getContentResolver()Landroid/content/ContentResolver;

move-result-object v34

const-string v35, "tweaks_enableAutoRecording"

const/16 v36, 0x0

invoke-static/range {v34 .. v36}, Landroid/provider/Settings$System;->getInt(Landroid/content/ContentResolver;Ljava/lang/String;I)I

move-result v4

if-eqz v4, :cond_33

const-string v34, "Falseclocks: recording tweak is enabled"

move-object/from16 v0, p0

move-object/from16 v1, v34

invoke-direct {v0, v1}, Lcom/android/phone/CallNotifier;->log(Ljava/lang/String;)V

invoke-static {}, Lcom/android/phone/util/VoiceRecorderHelper;->getInstance()Lcom/android/phone/util/VoiceRecorderHelper;

move-result-object v34

invoke-virtual/range {v34 .. v34}, Lcom/android/phone/util/VoiceRecorderHelper;->isRecording()Z

move-result v4

if-eqz v4, :cond_33

invoke-virtual/range {v34 .. v34}, Lcom/android/phone/util/VoiceRecorderHelper;->stop()Z

const-string v34, "Falseclock: automatic recording stopped"

move-object/from16 v0, p0

move-object/from16 v1, v34

invoke-direct {v0, v1}, Lcom/android/phone/CallNotifier;->log(Ljava/lang/String;)V

.line 2487
:cond_33

#---------------------------------------
# конец вживленного кода

move-object/from16 v0, p0

iget-object v0, v0, Lcom/android/phone/CallNotifier;->mCM:Lcom/android/internal/telephony/CallManager;

move-object/from16 v34, v0





Запретить энергосбережение




Патчить исходный код мне не пришлось, но в твикере реализовал следующий (урезанный для примера) программный код

try
{
if (value == 1)
{
Runtime.getRuntime().exec("su -c pm disable com.htc.htcpowermanager/.powersaver.PowerSaverNotificationReceiver");
} else {
Runtime.getRuntime().exec("su -c pm enable com.htc.htcpowermanager/.powersaver.PowerSaverNotificationReceiver");
}
}
catch (IOException e)
{
e.printStackTrace();
}


Запретить выключатели




Где хранятся эти
выключатели
image


мне пришлось потратить некоторое время. Узнать от куда растут ноги просто так не получится как в случае с «Уведомление о соединении», так как это стандартный интерфейс. Мне пришлось распаковать framework-res.apk, framework-htc-res.apk, com.htc.resources.apk, Phone.apk, Rosie.apk и SystemUI.apk. Как раз в SystemUI и оказались изображения и строки Wi-Fi, Bluetooth, Мобильный интернет и т.д.

Точно также как и в случае с уведомлениями…


Потрошим приложение



1. Кладем SystemUI.apk в папку place-apk-here-for-modding нашего APK-Multi-Tool.

2. Открываем любим архиватором и удаляем от туда файл classes.dex. Это ускорит работу и избавит вас от ошибок декомпилятора.

3. Запускаем скрипт Script.bat и выбираем 9-ый пункт Decompile apk. Нам нужно распаковать приложение и покопаться в файлах res/values. После распаковки исходники будут лежать в папке .\projects\SystemUI.apk
Поиски кода



1. Так как у меня интерфейс русский, то мне нужна папка с русскими словами .\res\values-ru.

2. На скриншоте из прошлой статьи видим, что у нас есть слово «В самолёте» и оно явно находится в нашей локализации.

3. Ищем по всем файлам наше слово… и находим

TOTAL: 3 matches in 1 file (1021 other files without matches are not listed)
3 matches in S:\dev\Android\APK-Multi-Tool\projects\SystemUI.apk\res\values-ru\strings.xml
22 <string name="status_bar_settings_airplane">Режим «В самолёте»</string>
97 <string name="accessibility_airplane_mode">Режим «В самолёте».</string>
182 <string name="status_Bar_quick_setting_airplane">Режим «В самолёте»</string>




4. Нас интересует status_Bar_quick_setting_airplane. Делаем поиск по этой строке.

TOTAL: 2 matches in 2 files (9 other files without matches are not listed)
1 match in S:\dev\Android\APK-Multi-Tool\projects\SystemUI.apk\res\values\public.xml
1040 <public type="string" name="status_Bar_quick_setting_airplane" id="0x7f0900b2" />
1 match in S:\dev\Android\APK-Multi-Tool\projects\SystemUI.apk\res\values\strings.xml
189 <string name="status_Bar_quick_setting_airplane">Airplane Mode</string>




5. Мы нашли шестнадцатиричный ID текстовой строки 0x7f0900b2, что в десятичном у нас 2131296434 (переводится в виндовом калькуляторе).

6. Берем наш classes.dex из SystemUI.apk, конвертируем в jar и открываем в gd-gui;

7. Открыв сконвертированный classes.dex в gd-gui, сохраним наш исходный код для поиска в нем.

8. Сделаем поиск 2131296434 в наших исходниках и… ничего не находим :-(

9. Делаем поиск по всей папке .\projects\SystemUI.apk\res\ и получаем следующие:

результаты поиска


TOTAL: 15 matches in 15 files (1007 other files without matches are not listed)
1 match in S:\dev\Android\APK-Multi-Tool\projects\SystemUI.apk\res\layout\status_bar_expanded_quick_setting.xml
35 <TextView android:gravity="center" android:id="@id/text_airplane" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="@string/status_Bar_quick_setting_airplane" android:lines="2" />
1 match in S:\dev\Android\APK-Multi-Tool\projects\SystemUI.apk\res\values\public.xml
1040 <public type="string" name="status_Bar_quick_setting_airplane" id="0x7f0900b2" />
1 match in S:\dev\Android\APK-Multi-Tool\projects\SystemUI.apk\res\values\strings.xml
189 <string name="status_Bar_quick_setting_airplane">Airplane Mode</string>
1 match in S:\dev\Android\APK-Multi-Tool\projects\SystemUI.apk\res\values-cs\strings.xml
182 <string name="status_Bar_quick_setting_airplane">Režim V letadle</string>
1 match in S:\dev\Android\APK-Multi-Tool\projects\SystemUI.apk\res\values-de\strings.xml
182 <string name="status_Bar_quick_setting_airplane">Flugmodus</string>
1 match in S:\dev\Android\APK-Multi-Tool\projects\SystemUI.apk\res\values-es\strings.xml
182 <string name="status_Bar_quick_setting_airplane">Modo avión</string>
1 match in S:\dev\Android\APK-Multi-Tool\projects\SystemUI.apk\res\values-fr\strings.xml
182 <string name="status_Bar_quick_setting_airplane">Mode avion</string>
1 match in S:\dev\Android\APK-Multi-Tool\projects\SystemUI.apk\res\values-it\strings.xml
182 <string name="status_Bar_quick_setting_airplane">Modalità aereo</string>
1 match in S:\dev\Android\APK-Multi-Tool\projects\SystemUI.apk\res\values-ja\strings.xml
184 <string name="status_Bar_quick_setting_airplane">フライトモード</string>
1 match in S:\dev\Android\APK-Multi-Tool\projects\SystemUI.apk\res\values-ko\strings.xml
184 <string name="status_Bar_quick_setting_airplane">비행 모드</string>
1 match in S:\dev\Android\APK-Multi-Tool\projects\SystemUI.apk\res\values-nl\strings.xml
182 <string name="status_Bar_quick_setting_airplane">Vliegtuigmodus</string>
1 match in S:\dev\Android\APK-Multi-Tool\projects\SystemUI.apk\res\values-pl\strings.xml
182 <string name="status_Bar_quick_setting_airplane">Tryb samolotowy</string>
1 match in S:\dev\Android\APK-Multi-Tool\projects\SystemUI.apk\res\values-ru\strings.xml
182 <string name="status_Bar_quick_setting_airplane">Режим «В самолёте»</string>
1 match in S:\dev\Android\APK-Multi-Tool\projects\SystemUI.apk\res\values-zh-rCN\strings.xml
184 <string name="status_Bar_quick_setting_airplane">飞行模式</string>
1 match in S:\dev\Android\APK-Multi-Tool\projects\SystemUI.apk\res\values-zh-rTW\strings.xml
184 <string name="status_Bar_quick_setting_airplane">飛安模式</string>





10. Из результатов понимаем, что для наших быстрых настроек есть готовый шаблон status_bar_expanded_quick_settin.xml

1 match in S:\dev\Android\APK-Multi-Tool\projects\SystemUI.apk\res\layout\status_bar_expanded_quick_setting.xml
35 <TextView android:gravity="center" android:id="@id/text_airplane" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="@string/status_Bar_quick_setting_airplane" android:lines="2" />




11. Открываем xmk файл и видим, что layout имеет ID layoutquicksetting

<HorizontalScrollView android:orientation="vertical" android:id="@id/layoutquicksetting" android:background="@drawable/notification_quick_settings_bkg" android:scrollbars="none" android:fadingEdge="none" android:layout_width="wrap_content" android:layout_height="fill_parent" android:overScrollMode="ifContentScrolls"
xmlns:android="http://ift.tt/nIICcg">




12. Ищем по layoutquicksetting и находим идентификатор 0x7f0c004c (2131492940)

результаты поиска


TOTAL: 3 matches in 3 files (1019 other files without matches are not listed)
1 match in S:\dev\Android\APK-Multi-Tool\projects\SystemUI.apk\res\layout\status_bar_expanded_quick_setting.xml
2 <HorizontalScrollView android:orientation="vertical" android:id="@id/layoutquicksetting" android:background="@drawable/notification_quick_settings_bkg" android:scrollbars="none" android:fadingEdge="none" android:layout_width="wrap_content" android:layout_height="fill_parent" android:overScrollMode="ifContentScrolls"
1 match in S:\dev\Android\APK-Multi-Tool\projects\SystemUI.apk\res\values\ids.xml
79 <item type="id" name="layoutquicksetting">false</item>
1 match in S:\dev\Android\APK-Multi-Tool\projects\SystemUI.apk\res\values\public.xml
1198 <public type="id" name="layoutquicksetting" id="0x7f0c004c" />





13. Ищем по исходникам, что получили в пункте 8 и опять не находим. Два раза не найти — вещь не стандартная. Из опыта знаем, что gd-gui не всегда умеет декомпилировать код и выдает // INTERNAL ERROR // , поэтому попробуем распаковать до smali.

14. Даем команду java -Xmx512m -jar baksmali.jar -a <API LEVEL> -d <FRAMEWORK DIR> -o SystemUI -x SystemUI.apk

<API LEVEL> — это API вашей версии Android. Для JB — это 16

<FRAMEWORK DIR> — папка, где находятся все фреймворки прошивки.


В моем случае это была команда

java -Xmx512m -jar baksmali.jar -a 16 -d S:\dev\Android\Android-Kitchen\WORKING_JB_15\system\framework -o SystemUI -x SystemUI.apk

15. В нашей вновь созданной папке появилась папка SystemUI, а в ней наши файлы с Dalvik кодом.

16. Ищем в коде строку 7f0c004c и находим ее в методе


updateQuickSettingView


.method private updateQuickSettingView()V
.registers 6

.prologue
const/4 v0, -0x2

.line 830
new-instance v1, Landroid/widget/LinearLayout$LayoutParams;

invoke-direct {v1, v0, v0}, Landroid/widget/LinearLayout$LayoutParams;-><init>(II)V

.line 832
iget-object v0, p0, Lcom/android/systemui/statusbar/phone/PhoneStatusBar;->mDisplayMetrics:Landroid/util/DisplayMetrics;

iget v0, v0, Landroid/util/DisplayMetrics;->widthPixels:I

div-int/lit8 v0, v0, 0x5

iput v0, v1, Landroid/view/ViewGroup$LayoutParams;->width:I

.line 834
iget-object v0, p0, Lcom/android/systemui/statusbar/phone/PhoneStatusBar;->mStatusBarWindow:Lcom/android/systemui/statusbar/phone/StatusBarWindowView;

const v2, 0x7f0c004c





Модификация кода



Анализируя Dalvik код понимаем, что метод проверяет текущие настройки и состояние железа и подставляет нужные иконки.

Чтобы убрать наш слой, мы просто его можем скрыть через метод setVisibility, поставив туда значение 8.

Готовый патч


.line 945
iget-object v0, p0, Lcom/android/systemui/statusbar/phone/PhoneStatusBar;->mRotationBtn:Landroid/widget/LinearLayout;

new-instance v1, Lcom/android/systemui/statusbar/phone/PhoneStatusBar$17;

invoke-direct {v1, p0}, Lcom/android/systemui/statusbar/phone/PhoneStatusBar$17;-><init>(Lcom/android/systemui/statusbar/phone/PhoneStatusBar;)V

invoke-virtual {v0, v1}, Landroid/widget/LinearLayout;->setOnClickListener(Landroid/view/View$OnClickListener;)V

.line 962
#---------------------------------------
# начало вживленного кода
iget-object v0, p0, Lcom/android/systemui/SystemUI;->mContext:Landroid/content/Context;

invoke-virtual {v0}, Landroid/content/Context;->getContentResolver()Landroid/content/ContentResolver;

move-result-object v0

const-string v1, "tweaks_disable_stock_qs"

const/4 v2, 0x0

invoke-static {v0, v1, v2}, Landroid/provider/Settings$System;->getInt(Landroid/content/ContentResolver;Ljava/lang/String;I)I

move-result v0

const/4 v2, 0x1

if-ne v0, v2, :cond_2de

iget-object v0, p0, Lcom/android/systemui/statusbar/phone/PhoneStatusBar;->mQuickSettingBar:Landroid/widget/HorizontalScrollView;

const/16 v2, 0x8

invoke-virtual {v0, v2}, Landroid/widget/HorizontalScrollView;->setVisibility(I)V

:cond_2de
#---------------------------------------
# конец вживленного кода

return-void
.end method





Заключение




В целом модификация прошивок весьма интересное и увлекательное занятие. Разобравшись однажды в принципах, вы сможете с легкой руки добавлять или избавляться от функционала в своем телефоне. Искренне надеюсь, что вам это нравится также как и мне.

This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.


Комментариев нет:

Отправить комментарий