Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Evolving the Android Core with Aspects

1,047 views

Published on

Using AOP / AspectJ to extend & customize the android core without actually changing any source code. Presented at Droidcon IT 2016.

Published in: Software
  • Be the first to comment

Evolving the Android Core with Aspects

  1. 1. Carlo Pescio @CarloPescio http://aspectroid.com Carlo Pescio Evolving the Android Core with Aspects
  2. 2. Carlo Pescio @CarloPescio http://aspectroid.com Android on custom devices New hardware: - Ethernet - TV tuner - Modbus Different target / product line / market: - Not a “personal device” - Keeping “user data” not an option - USB devices should autostart apps w/out asking user - Foreground app can’t win - Change low memory policies
  3. 3. Android architecture Looks familiar ? 
  4. 4. Carlo Pescio @CarloPescio http://aspectroid.com AudioManager int mBecomingNoisyIntentDevices = AudioSystem.DEVICE_OUT_WIRED_HEADSET | AudioSystem.DEVICE_OUT_WIRED_HEADPHONE | AudioSystem.DEVICE_OUT_ALL_A2DP | AudioSystem.DEVICE_OUT_HDMI | AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET | AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET | AudioSystem.DEVICE_OUT_ALL_USB | AudioSystem.DEVICE_OUT_LINE;
  5. 5. Carlo Pescio @CarloPescio http://aspectroid.com AudioManager private void onSetWiredDeviceConnectionState(int device, int state, String name) { synchronized (mConnectedDevices) { if((state == 0) && ((device == AudioSystem.DEVICE_OUT_WIRED_HEADSET) || (device == AudioSystem.DEVICE_OUT_WIRED_HEADPHONE)|| (device == AudioSystem.DEVICE_OUT_LINE))) { setBluetoothA2dpOnInt(true); } boolean isUsb = ((device & ~AudioSystem.DEVICE_OUT_ALL_USB) == 0) || (((device & AudioSystem.DEVICE_BIT_IN) != 0) && ((device & ~AudioSystem.DEVICE_IN_ALL_USB) == 0)); handleDeviceConnection((state == 1), device, (isUsb ? name : "")); if (state != 0) { if ((device == AudioSystem.DEVICE_OUT_WIRED_HEADSET) || (device == AudioSystem.DEVICE_OUT_WIRED_HEADPHONE) || (device == AudioSystem.DEVICE_OUT_LINE)) { setBluetoothA2dpOnInt(false); }
  6. 6. Carlo Pescio @CarloPescio http://aspectroid.com Evolving open source code Plugin architecture: lucky you Or: just fork & change the damn code pull + 3 way compare • Feature / Artifact symmetry is broken • Features are scattered in small patches • Product families => multiple forks
  7. 7. Carlo Pescio @CarloPescio http://aspectroid.com aspectroid exploring the aspect-oriented design space
  8. 8. Carlo Pescio @CarloPescio http://aspectroid.com Aspect Orientation Born to address Cross-Cutting, Scattered Concerns Error handling, Transaction handling Not very successful so far No design discipline evolved Core features: Customize behavior w/out changing code Avoid scattering a single concern around
  9. 9. Carlo Pescio @CarloPescio http://aspectroid.com AspectJ crash course Inter-type declaration: add data members, member functions, implement interfaces, … aspect ScreenStateSettingsAspect { private CheckBoxPreference DevelopmentSettings.stayOnPreference; }
  10. 10. Carlo Pescio @CarloPescio http://aspectroid.com AspectJ crash course Pointcut (the “where”) identify matching parts of code (sophisticated syntax) pointcut settingsUpdated() : execution( private void PowerManagerService.updateStayOnLocked(int)); pointcut adbSet() : within(UsbDeviceManager) && set(boolean *.mAdbEnabled);
  11. 11. Carlo Pescio @CarloPescio http://aspectroid.com AspectJ crash course Advice (the when and how) execute code before / after / around code matched by a pointcut (join point) after(boolean newVal) : adbSet() && args(newVal) { // do something here }
  12. 12. Carlo Pescio @CarloPescio http://aspectroid.com AspectJ crash course Aspect scoping mechanism; also: artifact privileged aspect ScreenStatePowerAspect { … inter-type declarations … pointcuts … advices … functions … data }
  13. 13. Carlo Pescio @CarloPescio http://aspectroid.com AspectJ & Android Apps: easy download plugin, add AspectJ nature to project Core: not so easy Shell calling Makefiles calling Python calling make Strong expectations (class files etc.) AJC can compile regular Java files aspects don’t need .aj extension, .java is ok
  14. 14. Carlo Pescio @CarloPescio http://aspectroid.com AspectJ in the core - Create a compiler wrapper around ajc in C - Standardizes options and behavior - Like: files in a file list must have absolute path - Minimal changes to the build scripts - If you link with the aspectjrt library, I call the compiler wrapper - Applicable to other JVM languages
  15. 15. # C o m m o n d e f i n i t i o n t o i n v o k e j a v a c o n t h e h o s t a n d t a r g e t . # # S o m e h i s t o r i c a l n o t e s : # - b e l o w w e w r i t e t h e l i s t o f j a v a f i l e s t o j a v a - s o u r c e - l i s t t o a v o i d a r g u m e n t # l i s t l e n g t h p r o b l e m s w i t h C y g w i n # - w e f i l t e r o u t d u p l i c a t e j a v a f i l e n a m e s b e c a u s e e c l i p s e ' s c o m p i l e r # d o e s n ' t l i k e t h e m . # # $ ( 1 ) : j a v a c # $ ( 2 ) : b o o t c l a s s p a t h d e f i n e c o m p i l e - j a v a $ ( h i d e ) r m - f $ @ $ ( h i d e ) r m - r f $ ( P R I V A T E _ C L A S S _ I N T E R M E D I A T E S _ D I R ) $ ( h i d e ) m k d i r - p $ ( d i r $ @ ) $ ( h i d e ) m k d i r - p $ ( P R I V A T E _ C L A S S _ I N T E R M E D I A T E S _ D I R ) $ ( c a l l u n z i p - j a r - f i l e s , $ ( P R I V A T E _ S T A T I C _ J A V A _ L I B R A R I E S ) , $ ( P R I V A T E _ C L A S S _ I N T E R M E D I A T E S _ D I R ) ) $ ( c a l l d u m p - w o r d s - t o - f i l e , $ ( P R I V A T E _ J A V A _ S O U R C E S ) , $ ( P R I V A T E _ C L A S S _ I N T E R M E D I A T E S _ D I R ) / j a v a - s o u r c e - l i s t ) $ ( h i d e ) i f [ - d " $ ( P R I V A T E _ S O U R C E _ I N T E R M E D I A T E S _ D I R ) " ] ; t h e n f i n d $ ( P R I V A T E _ S O U R C E _ I N T E R M E D I A T E S _ D I R ) - n a m e ' * . j a v a ' > > $ ( P R I V A T E _ C L A S S _ I N T E R M E D I A T E S _ D I R ) / j a v a - s o u r c e - l i s t ; f i $ ( h i d e ) t r ' ' ' n ' < $ ( P R I V A T E _ C L A S S _ I N T E R M E D I A T E S _ D I R ) / j a v a - s o u r c e - l i s t | s o r t - u > $ ( P R I V A T E _ C L A S S _ I N T E R M E D I A T E S _ D I R ) / j a v a - s o u r c e - l i s t - u n i q $ ( h i d e ) i f [ - s $ ( P R I V A T E _ C L A S S _ I N T E R M E D I A T E S _ D I R ) / j a v a - s o u r c e - l i s t - u n i q ] ; t h e n $ ( i f $ ( f i n d s t r i n g a s p e c t j r t , $ ( P R I V A T E _ S T A T I C _ J A V A _ L I B R A R I E S ) ) , $ ( s u b s t j a v a c , a j c w , $ ( 1 ) ) , $ ( 1 ) ) - e n c o d i n g U T F - 8 $ ( s t r i p $ ( P R I V A T E _ J A V A C _ D E B U G _ F L A G S ) ) $ ( i f $ ( f i n d s t r i n g t r u e , $ ( P R I V A T E _ W A R N I N G S _ E N A B L E ) ) , $ ( x l i n t _ u n c h e c k e d ) , ) $ ( 2 ) $ ( a d d p r e f i x - c l a s s p a t h , $ ( s t r i p $ ( c a l l n o r m a l i z e - p a t h - l i s t , $ ( P R I V A T E _ A L L _ J A V A _ L I B R A R I E S ) ) ) ) $ ( i f $ ( f i n d s t r i n g t r u e , $ ( P R I V A T E _ W A R N I N G S _ E N A B L E ) ) , $ ( x l i n t _ u n c h e c k e d ) , ) - e x t d i r s " " - d $ ( P R I V A T E _ C L A S S _ I N T E R M E D I A T E S _ D I R ) $ ( P R I V A T E _ J A V A C F L A G S ) @ $ ( P R I V A T E _ C L A S S _ I N T E R M E D I A T E S _ D I R ) / j a v a - s o u r c e - l i s t - u n i q | | ( r m - r f $ ( P R I V A T E _ C L A S S _ I N T E R M E D I A T E S _ D I R ) ; e x i t 4 1 ) f i $ ( i f $ ( P R I V A T E _ J A V A _ L A Y E R S _ F I L E ) , $ ( h i d e ) b u i l d / t o o l s / j a v a - l a y e r s . p y $ ( P R I V A T E _ J A V A _ L A Y E R S _ F I L E ) @ $ ( P R I V A T E _ C L A S S _ I N T E R M E D I A T E S _ D I R ) / j a v a - s o u r c e - l i s t - u n i q , ) $ ( h i d e ) r m - f $ ( P R I V A T E _ C L A S S _ I N T E R M E D I A T E S _ D I R ) / j a v a - s o u r c e - l i s t $ ( h i d e ) r m - f $ ( P R I V A T E _ C L A S S _ I N T E R M E D I A T E S _ D I R ) / j a v a - s o u r c e - l i s t - u n i q $ ( i f $ ( P R I V A T E _ J A R _ E X C L U D E _ F I L E S ) , $ ( h i d e ) f i n d $ ( P R I V A T E _ C L A S S _ I N T E R M E D I A T E S _ D I R ) - n a m e $ ( w o r d 1 , $ ( P R I V A T E _ J A R _ E X C L U D E _ F I L E S ) ) $ ( a d d p r e f i x - o - n a m e , $ ( w o r d l i s t 2 , 9 9 9 , $ ( P R I V A T E _ J A R _ E X C L U D E _ F I L E S ) ) ) | x a r g s r m - r f ) $ ( i f $ ( P R I V A T E _ J A R _ P A C K A G E S ) , $ ( h i d e ) f i n d $ ( P R I V A T E _ C L A S S _ I N T E R M E D I A T E S _ D I R ) - m i n d e p t h 1 - t y p e f $ ( f o r e a c h p k g , $ ( P R I V A T E _ J A R _ P A C K A G E S ) , - n o t - p a t h $ ( P R I V A T E _ C L A S S _ I N T E R M E D I A T E S _ D I R ) / $ ( s u b s t . , / , $ ( p k g ) ) / * ) - d e l e t e ; f i n d $ ( P R I V A T E _ C L A S S _ I N T E R M E D I A T E S _ D I R ) - e m p t y - d e l e t e ) $ ( i f $ ( P R I V A T E _ J A R _ E X C L U D E _ P A C K A G E S ) , $ ( h i d e ) r m - r f $ ( f o r e a c h p k g , $ ( P R I V A T E _ J A R _ E X C L U D E _ P A C K A G E S ) , $ ( P R I V A T E _ C L A S S _ I N T E R M E D I A T E S _ D I R ) / $ ( s u b s t . , / , $ ( p k g ) ) ) ) $ ( i f $ ( P R I V A T E _ R M T Y P E D E F S ) , $ ( h i d e ) $ ( R M T Y P E D E F S ) - v $ ( P R I V A T E _ C L A S S _ I N T E R M E D I A T E S _ D I R ) ) $ ( i f $ ( P R I V A T E _ J A R _ M A N I F E S T ) , $ ( h i d e ) s e d - e ' s / % B U I L D _ N U M B E R % / $ ( B U I L D _ N U M B E R ) / ' $ ( P R I V A T E _ J A R _ M A N I F E S T ) > $ ( d i r $ @ ) / m a n i f e s t . m f & & j a r - c f m $ @ $ ( d i r $ @ ) / m a n i f e s t . m f - C $ ( P R I V A T E _ C L A S S _ I N T E R M E D I A T E S _ D I R ) . , $ ( h i d e ) j a r - c f $ @ - C $ ( P R I V A T E _ C L A S S _ I N T E R M E D I A T E S _ D I R ) . ) e n d e f AspectJ in the core Definitions.mk -> define compile-java $(if $(findstring aspectjrt, $(PRIVATE_STATIC_JAVA_LIBRARIES)), $(subst javac,ajcw,$(1)), $(1))
  16. 16. Does it actually work? Add a useful feature without changing sources
  17. 17. Carlo Pescio @CarloPescio http://aspectroid.com How do we start? • Explore code • “Stay on while charging” • UsbDeviceManager, PowerManagerService • See ebook for details on the quest • Tested as much as possible outside the core • See ebook again • Add aspects • Make aspects robust !
  18. 18. A map
  19. 19. A map
  20. 20. A map
  21. 21. A map
  22. 22. Carlo Pescio @CarloPescio http://aspectroid.com Extend the Settings app - Add a new checkbox - Persist the checkbox state somewhere - Show the persisted value on resume
  23. 23. Settings Aspect (1/3) privileged aspect ScreenStateSettingsAspect { private CheckBoxPreference DevelopmentSettings.stayOnPreference; pointcut onCreateExecuted() : execution(public void DevelopmentSettings.onCreate(..)) ; pointcut onResumeExecuted() : execution(public void DevelopmentSettings.onResume(..)) ;
  24. 24. Settings Aspect (2/3) after(DevelopmentSettings self):onCreateExecuted()&&target(self) { final Context ctx = self.getActivity(); PreferenceCategory targetCategory = (PreferenceCategory)self. findPreference(DevelopmentSettings.DEBUG_DEBUGGING_CATEGORY_KEY); self.stayOnPreference = new CheckBoxPreference(ctx); self.stayOnPreference.setKey("aspectroidScreenState"); self.stayOnPreference.setTitle("Prevent sleeping while debugging"); self.stayOnPreference.setSummary("only for usb debugging"); targetCategory.addPreference(self.stayOnPreference); self.stayOnPreference.setOnPreferenceChangeListener( new OnPreferenceChangeListener() { public boolean onPreferenceChange( Preference preference, Object newValue) { API.persistPreference(ctx, (Boolean) newValue); return true; } }); }
  25. 25. Settings Aspect (3/3) after(DevelopmentSettings self) : onResumeExecuted() && target(self) { final Context ctx = self.getActivity(); boolean on = API.retrievePreference(ctx); self.stayOnPreference.setChecked(on); } } 
  26. 26. Carlo Pescio @CarloPescio http://aspectroid.com Am I debugging? - USB debugging enabled - USB connected to a computer - Not just charging - Not in OTG / host mode
  27. 27. Carlo Pescio @CarloPescio http://aspectroid.com UsbDeviceManager See ebook for details
  28. 28. USB Aspect (1/3) aspect ScreenStateUsbAspect { boolean adbEnabled = false; boolean adbConnected = false; Context context; before(Context ctx): execution(UsbDeviceManager.new(Context)) && args( ctx ) { context = ctx; }
  29. 29. USB Aspect (2/3) pointcut adbSet() : within(UsbDeviceManager) && set(boolean *.mAdbEnabled); after(boolean newVal) : adbSet() && args(newVal) { adbEnabled = newVal; checkAdbOn(); }
  30. 30. USB Aspect (3/3) pointcut connectedSet() : within(UsbDeviceManager) && set(boolean *.mConnected); after(boolean newVal) : connectedSet() && args(newVal) { adbConnected = newVal; checkAdbOn(); } void checkAdbOn() { boolean isOn = adbEnabled && adbConnected; API.storeAdbState(context, isOn); } } 
  31. 31. PowerManagerService (1) public void systemReady(IAppOpsService appOps) { // … final ContentResolver resolver = mContext.getContentResolver(); // … resolver.registerContentObserver( Settings.Global.getUriFor( Settings.Global.STAY_ON_WHILE_PLUGGED_IN), false, mSettingsObserver, UserHandle.USER_ALL); //… updateSettingsLocked(); //… }
  32. 32. PowerManagerService (2) private void updateStayOnLocked(int dirty) { if( (dirty & (DIRTY_BATTERY_STATE | DIRTY_SETTINGS)) != 0 ) { final boolean wasStayOn = mStayOn; if( mStayOnWhilePluggedInSetting != 0 && ! isMaximumScreenOffTimeoutFromDeviceAdminEnforcedLocked()){ mStayOn = mBatteryManagerInternal.isPowered( mStayOnWhilePluggedInSetting); } else { mStayOn = false; } if( mStayOn != wasStayOn ) { mDirty |= DIRTY_STAY_ON; } } }
  33. 33. Power Aspect (1/2) privileged aspect ScreenStatePowerAspect { pointcut systemReadyExecuted() : execution( public void PowerManagerService.systemReady(..)); after(PowerManagerService self) : systemReadyExecuted() && target(self) { API.registerScreenOnObserver(self.mContext, self.mSettingsObserver); }
  34. 34. Power Aspect (2/2) pointcut settingsUpdated() : execution( private void PowerManagerService.updateStayOnLocked(int)); void around(PowerManagerService self, int dirty) : settingsUpdated() && args(dirty) && target(self) { proceed(self, dirty); boolean keepOn = API.shouldKeepScrenOn(self.mContext); if( keepOn && !self.mStayOn ) { self.mStayOn = true; self.mDirty |= PowerManagerService.DIRTY_STAY_ON; } } } 
  35. 35. Carlo Pescio @CarloPescio http://aspectroid.com Is it worth doing? Core of science: experiments & falsifiability port the aspects from 5.0 to 6.0 Hardest candidate: PowerManagerService Power mgmt changed in 6.0 to include Doze Also the most fragile of my aspects
  36. 36. Carlo Pescio @CarloPescio http://aspectroid.com File compare map 5.0 vs 6.0
  37. 37. Carlo Pescio @CarloPescio http://aspectroid.com Still working? Class: PowerManagerService still there Member functions: systemReady updateStayOnLocked Data members: mContext, mSettingsObserver, mStayOn, mDirty still there, same purpose & signature still there, same purpose 
  38. 38. Carlo Pescio @CarloPescio http://aspectroid.com Conclusions: core benefits - Feature traceability and selection - Features as artifacts, not changesets - Simplified maintenance / porting
  39. 39. Carlo Pescio @CarloPescio http://aspectroid.com Challenges Aspectj is still “one project at a time” Core is not aspect-friendly very long methods, hard to pointcut inside Core itself would actually benefit A LOT from AOP ok google?  Rejection of the unfamiliar 3-way compare is a known pain branch X product in a product family is a known pain
  40. 40. Carlo Pescio @CarloPescio http://aspectroid.com Why learning this stuff? - New ways to solve problems Customize FOSS by adding code - New ways to structure your code aspect-friendly mix OOP and AOP - In the end: you maintain LESS code [good] aspects can be quite resilient
  41. 41. Carlo Pescio @CarloPescio http://aspectroid.com Get in touch carlo.pescio@gmail.com @CarloPescio http://eptacom.net http://aspectroid.com

×