It is given: there is an application Ultimate Call Screen HD - there are quite a few similar applications on the market.
Question: how does it work? How is it written?
Who has any ideas?
PS Focusing on non-rooted devices.
It is given: there is an application Ultimate Call Screen HD - there are quite a few similar applications on the market.
Question: how does it work? How is it written?
Who has any ideas?
PS Focusing on non-rooted devices.
TL; DR
The application is presented to the headset system in order to receive / end calls
The Ultimate Call Screen HD application in the manifest states the following:
<uses-permission android:name="android.permission.DISABLE_KEYGUARD"/> <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/> <uses-permission android:name="android.permission.READ_CONTACTS"/> <uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS"/> <uses-permission android:name="android.permission.CALL_PHONE"/> <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/> <application ...> <service android:enabled="true" android:name="com.lowveld.ucs.service.OutcallService"/> <service android:enabled="true" android:name="com.lowveld.ucs.service.InCallService"/> <receiver android:enabled="true" android:name="com.lowveld.ucs.receivers.PhoneReceiver"> <intent-filter android:priority="2147483647"> <action android:name="android.intent.action.PHONE_STATE"/> <action android:name="android.intent.action.NEW_OUTGOING_CALL"/> <action android:name="com.lowveld.ucs.action.INDIRECT_CALL"/> </intent-filter> </receiver> ... </application> From the manifest, you can see that PhoneReceiver receives the first intent from the system about the outgoing / incoming call. It then delegates processing to the InCallService and OutcallService , which display the necessary interface for management and use AudioManager for voice transmission.
UPDATE: Open the InCallService class
public class InCallService extends Service implements SensorEventListener, a, b, e { ... static boolean H; WindowManager G; WindowManager.LayoutParams I; CallWindowView J; ... private void e(final boolean b) { if (b) { return; } this.G.addView((View)this.J, (ViewGroup$LayoutParams)this.I); InCallService.H = true; } private void f(final boolean b) { if (!b) { return; } if (this.J.getWindowToken() != null) { this.G.removeView((View)this.J); } InCallService.H = false; } private void t() { this.I = new WindowManager.LayoutParams(-1, -1, 2010, 2621600, -1); if (ga("hide_status_bar", true)) { final WindowManager.LayoutParams i = this.I; i.flags |= 0x100; } else { this.I.type = 2003; } this.I.gravity = 80; if (ga("force_full_brightness", true)) { this.I.screenBrightness = 1.0f; } (this.J = (CallWindowView)((LayoutInflater)this.getSystemService("layout_inflater")).inflate(2130903099, (ViewGroup)null)).a(new a(this)); this.G = (WindowManager)this.getSystemService("window"); } ... } From this code, it is clear that the application covers the standard dialer window with its CallWindowView (for this, the permission android.permission.SYSTEM_ALERT_WINDOW required). Markup for an incoming call is stored in R.layout.two_button_frame = 2130903099
<?xml version="1.0" encoding="utf-8"?> <com.lowveld.ucs.ui.views.CallWindowView android:id="@id/callprompt_frame" ...> ... <RelativeLayout android:orientation="vertical" android:id="@id/callprompt" ...> <RelativeLayout android:orientation="vertical" android:id="@id/buttonblock" ...> ... <Button android:id="@id/answerbutton" android:text="@string/button_answer_call" ... /> <Button android:id="@id/rejectbutton" android:text="@string/button_decline_call" ... /> </RelativeLayout> ... </RelativeLayout> </com.lowveld.ucs.ui.views.CallWindowView> R.id.answerbutton = 2131558590
R.id.rejectbutton = 2131558495
The service has the code:
public void onCreate() { ... this.t(); this.at = (AudioManager)this.al.getSystemService("audio"); ... this.f = (Button)this.J.findViewById(2131558495); this.e = (Button)this.J.findViewById(2131558590); ... this.J.setOnTouchListener((View$OnTouchListener)new ac(this)); this.J.setOnKeyListener((View$OnKeyListener)new ad(this)); this.f.setOnClickListener((View$OnClickListener)new ae(this)); this.f.setOnLongClickListener((View$OnLongClickListener)new af(this)); this.e.setOnClickListener((View$OnClickListener)new b(this)); this.e.setOnLongClickListener((View$OnLongClickListener)new c(this)); ... } When you click the "Accept the call" button, the listener fires
class b implements View$OnClickListener { final InCallService a; b(final InCallService a) { super(); this.a = a; } public void onClick(final View view) { ... if (this.aa(this.a.al)) { this.a.ak.a(this.a.al); ... } else { final Intent intent = new Intent("android.intent.action.VIEW"); intent.setData(Uri.parse("market://details?id=com.lowveld.ucshdlicense")); intent.setFlags(268435456); this.a.startActivity(intent); this.ad(); } ... } } Which transfers control to this class:
public class a { Boolean a; Boolean b; public a() { super(); this.a = false; this.b = false; } private void a(final Context context, final int n, final boolean b) { new Thread(new b(this, n, b, context)).start(); } private void a(final Context context, final boolean b) { this.a = ga("isHeadsetOn", false); this.b = ((AudioManager)context.getSystemService("audio")).isWiredHeadsetOn(); while (true) { Label_0065: { if (!ga("root_activate_answer", false)) { //можно ли использовать рут-права break Label_0065; } try { this.b(); final int n = 0; if (n != 0) { this.b(context, b); //рут-права отсутствуют/нельзя использовать } return; } catch (Exception ex) { final int n = 1; continue; } } final int n = 1; continue; } } private void b(final Context context, final boolean b) { if (context != null) { if (!jb()) { API < 16 final Intent intent = new Intent("android.intent.action.HEADSET_PLUG"); intent.addFlags(1073741824); intent.putExtra("state", 2); intent.putExtra("name", "Headset"); context.sendOrderedBroadcast(intent, (String)null); } final Intent intent2 = new Intent("android.intent.action.MEDIA_BUTTON"); intent2.putExtra("android.intent.extra.KEY_EVENT", (Parcelable)new KeyEvent(0, 79)); // шлем действие ACTION_DOWN с кодом KEYCODE_HEADSETHOOK context.sendBroadcast(intent2, "android.permission.CALL_PRIVILEGED"); final Intent intent3 = new Intent("android.intent.action.MEDIA_BUTTON"); intent3.putExtra("android.intent.extra.KEY_EVENT", (Parcelable)new KeyEvent(1, 79)); // шлем действие ACTION_UP с кодом KEYCODE_HEADSETHOOK context.sendBroadcast(intent3, "android.permission.CALL_PRIVILEGED"); if (!this.a && !this.b && !jb()) { final Intent intent4 = new Intent("android.intent.action.HEADSET_PLUG"); intent4.addFlags(1073741824); intent4.putExtra("state", 0); intent4.putExtra("name", "Headset"); context.sendOrderedBroadcast(intent4, (String)null); } if (b && jb()) { return; } } } void a() { try { final DataOutputStream dataOutputStream = new DataOutputStream(Runtime.getRuntime().exec("su").getOutputStream()); dataOutputStream.writeBytes("input keyevent " + Integer.toString(6) + "\n"); dataOutputStream.writeBytes("exit\n"); dataOutputStream.flush(); dataOutputStream.close(); } catch (Exception ex) { ex.printStackTrace(); throw ex; } } public void a(final Context context) { //вызывается из слушателя if (ja()) { // API >= 21 this.a(context, 79, true); return; } this.a(context, true); } void b() { try { final DataOutputStream dataOutputStream = new DataOutputStream(Runtime.getRuntime().exec("su").getOutputStream()); dataOutputStream.writeBytes("input keyevent " + Integer.toString(5) + "\n"); //поднимает трубку dataOutputStream.writeBytes("exit\n"); dataOutputStream.flush(); dataOutputStream.close(); } catch (Exception ex) { ex.printStackTrace(); throw ex; } } ... } InCallService and OutCallService find the way to the dialer interface itself? The root is in this. And the program does not change the functionality of the dialer itself, but only replaces the buttons and the background. - BarmaleyI do not know how this is done in this application to which you refer. However, if I wanted to do something like this, I would do this:
Here's another one - https://stackoverflow.com/questions/5990590/how-to-detect-phone-call-broadcast-receiver-in-android
Create a beautiful custom view to show what is happening. Make settings that allow you to select a photo by phone number, and set it in the background, place the buttons on the top / bottom, etc. Here you can show the imagination and skills of the interface designer - do what you want, add social. networks, photos, avatars, ratings - what you want, and add that.
Handle user actions, similar to the default application.
In one of the comments you write this:
And the program does not change the functionality of the dialer itself, but only replaces the buttons and the background.
I would say that the program does not replace the buttons and background, but completely repeats the functionality of the "dialer". They just recreated everything that was, added styles and other settings, so that the end user can change what he wants when he wants.
View into the place of a dialer 2) You cannot replace the system dialer (without root) 3 ) The hack you are describing about android:priority is known for 100 years, although it doesn’t work in new versions 4) Tales about what's with If on some device the maximum priority is already set - your application on such a device will simply not be called - leave it on your conscience. - BarmaleySo there, in my opinion, a separate special microcontroller processes the audio stream from the call and this is not the ARM processor on which the device itself actually works. To him, even through the NDK can not obviously get through.
And through Jav, it seems that you can only change the standard interface and use Intent.ACTION_CALL as it is written there, and as it seems, it makes Ultimate Call Screen HD too.
Here are examples of threads where this was discussed:
https://groups.google.com/forum/#!topic/android-ndk/HNA-j0KnfII
https://groups.google.com/forum/#!msg/android-developers/AbU85mtDgQw/q_V_ACV1724J
True, they are from 2009, but I do not think that the situation with iron has suddenly changed.
Here is a description of the bus on which the audio transmission is carried out:
Source: https://ru.stackoverflow.com/questions/416547/
All Articles