亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 系統(tǒng) > Android > 正文

Android中的Notification機(jī)制深入理解

2019-10-21 21:25:08
字體:
供稿:網(wǎng)友

本文需要解決的問題

筆者最近正在做一個項(xiàng)目,里面需要用到 Android Notification 機(jī)制來實(shí)現(xiàn)某些特定需求。我正好通過這個機(jī)會研究一下 Android Notification 相關(guān)的發(fā)送邏輯和接收邏輯,以及整理相關(guān)的筆記。我研究 Notification 機(jī)制的目的是解決以下我在使用過程中所思考的問題:

  • 我們創(chuàng)建的 Notification 實(shí)例最終以什么樣的方式發(fā)送給系統(tǒng)?
  • 系統(tǒng)是如何接收到 Notification 實(shí)例并顯示的?
  • 我們是否能攔截其他 app 的 Notification 并獲取其中的信息?

什么是 Android Notification 機(jī)制?

Notification,中文名翻譯為通知,每個 app 可以自定義通知的樣式和內(nèi)容等,它會顯示在系統(tǒng)的通知欄等區(qū)域。用戶可以打開抽屜式通知欄查看通知的詳細(xì)信息。在實(shí)際生活中,Android Notification 機(jī)制有很廣泛的應(yīng)用,例如 IM app 的新消息通知,資訊 app 的新聞推送等等。

源碼分析

本文的源碼基于 Android 7.0。

Notification 的發(fā)送邏輯

一般來說,如果我們自己的 app 想發(fā)送一條新的 Notification,可以參照以下代碼:

NotificationCompat.Builder mBuilder =  new NotificationCompat.Builder(this)  .setSmallIcon(R.drawable.notification_icon)  .setWhen(System.currentTimeMillis())  .setContentTitle("Test Notification Title")  .setContentText("Test Notification Content!");Intent resultIntent = new Intent(this, ResultActivity.class);PendingIntent contentIntent =  PendingIntent.getActivity(   this,    0,    resultIntent,    PendingIntent.FLAG_UPDATE_CURRENT  );mBuilder.setContentIntent(resultPendingIntent);NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);// mId allows you to update the notification later on.mNotificationManager.notify(mId, mBuilder.build());

可以看到,我們通過 NotificationCompat.Builder 新建了一個 Notification 對象,最后通過 NotificationManager#notify() 方法將 Notification 發(fā)送出去。

NotificationManager#notify()

public void notify(int id, Notification notification){ notify(null, id, notification);}// 省略部分注釋public void notify(String tag, int id, Notification notification){ notifyAsUser(tag, id, notification, new UserHandle(UserHandle.myUserId()));}/** * @hide */public void notifyAsUser(String tag, int id, Notification notification, UserHandle user){ int[] idOut = new int[1]; INotificationManager service = getService(); String pkg = mContext.getPackageName(); // Fix the notification as best we can. Notification.addFieldsFromContext(mContext, notification); if (notification.sound != null) {  notification.sound = notification.sound.getCanonicalUri();  if (StrictMode.vmFileUriExposureEnabled()) {   notification.sound.checkFileUriExposed("Notification.sound");  } } fixLegacySmallIcon(notification, pkg); if (mContext.getApplicationInfo().targetSdkVersion > Build.VERSION_CODES.LOLLIPOP_MR1) {  if (notification.getSmallIcon() == null) {   throw new IllegalArgumentException("Invalid notification (no valid small icon): "     + notification);  } } if (localLOGV) Log.v(TAG, pkg + ": notify(" + id + ", " + notification + ")"); final Notification copy = Builder.maybeCloneStrippedForDelivery(notification); try {  // !!!  service.enqueueNotificationWithTag(pkg, mContext.getOpPackageName(), tag, id,    copy, idOut, user.getIdentifier());  if (localLOGV && id != idOut[0]) {   Log.v(TAG, "notify: id corrupted: sent " + id + ", got back " + idOut[0]);  } } catch (RemoteException e) {  throw e.rethrowFromSystemServer(); }}

我們可以看到,到最后會調(diào)用 service.enqueueNotificationWithTag() 方法,這里的是 service 是 INotificationManager 接口。如果熟悉 AIDL 等系統(tǒng)相關(guān)運(yùn)行機(jī)制的話,就可以看出這里是代理類調(diào)用了代理接口的方法,實(shí)際方法實(shí)現(xiàn)是在 NotificationManagerService 當(dāng)中。

NotificationManagerService#enqueueNotificationWithTag()

@Overridepublic void enqueueNotificationWithTag(String pkg, String opPkg, String tag, int id,   Notification notification, int[] idOut, int userId) throws RemoteException { enqueueNotificationInternal(pkg, opPkg, Binder.getCallingUid(),   Binder.getCallingPid(), tag, id, notification, idOut, userId);}void enqueueNotificationInternal(final String pkg, final String opPkg, final int callingUid,  final int callingPid, final String tag, final int id, final Notification notification,  int[] idOut, int incomingUserId) { if (DBG) {  Slog.v(TAG, "enqueueNotificationInternal: pkg=" + pkg + " id=" + id    + " notification=" + notification); } checkCallerIsSystemOrSameApp(pkg); final boolean isSystemNotification = isUidSystem(callingUid) || ("android".equals(pkg)); final boolean isNotificationFromListener = mListeners.isListenerPackage(pkg); final int userId = ActivityManager.handleIncomingUser(callingPid,   callingUid, incomingUserId, true, false, "enqueueNotification", pkg); final UserHandle user = new UserHandle(userId); // Fix the notification as best we can. try {  final ApplicationInfo ai = getContext().getPackageManager().getApplicationInfoAsUser(    pkg, PackageManager.MATCH_DEBUG_TRIAGED_MISSING,    (userId == UserHandle.USER_ALL) ? UserHandle.USER_SYSTEM : userId);  Notification.addFieldsFromContext(ai, userId, notification); } catch (NameNotFoundException e) {  Slog.e(TAG, "Cannot create a context for sending app", e);  return; } mUsageStats.registerEnqueuedByApp(pkg); if (pkg == null || notification == null) {  throw new IllegalArgumentException("null not allowed: pkg=" + pkg    + " id=" + id + " notification=" + notification); } final StatusBarNotification n = new StatusBarNotification(   pkg, opPkg, id, tag, callingUid, callingPid, 0, notification,   user); // Limit the number of notifications that any given package except the android // package or a registered listener can enqueue. Prevents DOS attacks and deals with leaks. if (!isSystemNotification && !isNotificationFromListener) {  synchronized (mNotificationList) {   if(mNotificationsByKey.get(n.getKey()) != null) {    // this is an update, rate limit updates only    final float appEnqueueRate = mUsageStats.getAppEnqueueRate(pkg);    if (appEnqueueRate > mMaxPackageEnqueueRate) {     mUsageStats.registerOverRateQuota(pkg);     final long now = SystemClock.elapsedRealtime();     if ((now - mLastOverRateLogTime) > MIN_PACKAGE_OVERRATE_LOG_INTERVAL) {      Slog.e(TAG, "Package enqueue rate is " + appEnqueueRate        + ". Shedding events. package=" + pkg);       mLastOverRateLogTime = now;     }     return;    }   }   int count = 0;   final int N = mNotificationList.size();   for (int i=0; i<N; i++) {    final NotificationRecord r = mNotificationList.get(i);    if (r.sbn.getPackageName().equals(pkg) && r.sbn.getUserId() == userId) {     if (r.sbn.getId() == id && TextUtils.equals(r.sbn.getTag(), tag)) {      break; // Allow updating existing notification     }     count++;     if (count >= MAX_PACKAGE_NOTIFICATIONS) {      mUsageStats.registerOverCountQuota(pkg);      Slog.e(TAG, "Package has already posted " + count        + " notifications. Not showing more. package=" + pkg);      return;     }    }   }  } } // Whitelist pending intents. if (notification.allPendingIntents != null) {  final int intentCount = notification.allPendingIntents.size();  if (intentCount > 0) {   final ActivityManagerInternal am = LocalServices     .getService(ActivityManagerInternal.class);   final long duration = LocalServices.getService(     DeviceIdleController.LocalService.class).getNotificationWhitelistDuration();   for (int i = 0; i < intentCount; i++) {    PendingIntent pendingIntent = notification.allPendingIntents.valueAt(i);    if (pendingIntent != null) {     am.setPendingIntentWhitelistDuration(pendingIntent.getTarget(), duration);    }   }  } } // Sanitize inputs notification.priority = clamp(notification.priority, Notification.PRIORITY_MIN,   Notification.PRIORITY_MAX); // setup local book-keeping final NotificationRecord r = new NotificationRecord(getContext(), n); mHandler.post(new EnqueueNotificationRunnable(userId, r)); idOut[0] = id;}

這里代碼比較多,但通過注釋可以清晰地理清整個邏輯:

  • 首先檢查通知發(fā)起者是系統(tǒng)進(jìn)程或者是查看發(fā)起者發(fā)送的是否是同個 app 的通知信息,否則拋出異常;
  • 除了系統(tǒng)的通知和已注冊的監(jiān)聽器允許入隊(duì)列外,其他 app 的通知都會限制通知數(shù)上限和通知頻率上限;
  • 將 notification 的 PendingIntent 加入到白名單;
  • 將之前的 notification 進(jìn)一步封裝為 StatusBarNotification 和 NotificationRecord,最后封裝到一個異步線程 EnqueueNotificationRunnable 中

這里有一個點(diǎn),就是 mHandler,涉及到切換線程,我們先跟蹤一下 mHandler 是在哪個線程被創(chuàng)建。

mHandler 是 WorkerHandler 類的一個實(shí)例,在 NotificationManagerService#onStart() 方法中被創(chuàng)建,而 NotificationManagerService 是系統(tǒng) Service,所以 EnqueueNotificationRunnable 的 run 方法會運(yùn)行在 system_server 的主線程。

NotificationManagerService.EnqueueNotificationRunnable#run()

@Overridepublic void run() { synchronized(mNotificationList) {  // 省略代碼  if (notification.getSmallIcon() != null) {   StatusBarNotification oldSbn = (old != null) ? old.sbn : null;   mListeners.notifyPostedLocked(n, oldSbn);  } else {   Slog.e(TAG, "Not posting notification without small icon: " + notification);   if (old != null && !old.isCanceled) {    mListeners.notifyRemovedLocked(n);   }   // ATTENTION: in a future release we will bail out here   // so that we do not play sounds, show lights, etc. for invalid   // notifications   Slog.e(TAG, "WARNING: In a future release this will crash the app: " + n.getPackageName());  }  buzzBeepBlinkLocked(r); }}
  1. 省略的代碼主要的工作是提取 notification 相關(guān)的屬性,同時通知 notification ranking service,有新的 notification 進(jìn)來,然后對所有 notification 進(jìn)行重新排序;
  2. 然后到最后會調(diào)用 mListeners.notifyPostedLocked() 方法。這里 mListeners 是 NotificationListeners 類的一個實(shí)例。

NotificationManagerService.NotificationListeners#notifyPostedLocked()  -> NotificationManagerService.NotificationListeners#notifyPosted()

public void notifyPostedLocked(StatusBarNotification sbn, StatusBarNotification oldSbn) { // Lazily initialized snapshots of the notification. TrimCache trimCache = new TrimCache(sbn); for (final ManagedServiceInfo info: mServices) {  boolean sbnVisible = isVisibleToListener(sbn, info);  boolean oldSbnVisible = oldSbn != null ? isVisibleToListener(oldSbn, info) : false;  // This notification hasn't been and still isn't visible -> ignore.  if (!oldSbnVisible && !sbnVisible) {   continue;  }  final NotificationRankingUpdate update = makeRankingUpdateLocked(info);  // This notification became invisible -> remove the old one.  if (oldSbnVisible && !sbnVisible) {   final StatusBarNotification oldSbnLightClone = oldSbn.cloneLight();   mHandler.post(new Runnable() {    @Override    public void run() {     notifyRemoved(info, oldSbnLightClone, update);    }   });   continue;  }  final StatusBarNotification sbnToPost = trimCache.ForListener(info);  mHandler.post(new Runnable() {   @Override   public void run() {    notifyPosted(info, sbnToPost, update);   }  }); }}private void notifyPosted(final ManagedServiceInfo info, final StatusBarNotification sbn, NotificationRankingUpdate rankingUpdate) { final INotificationListener listener = (INotificationListener) info.service; StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn); try {  listener.onNotificationPosted(sbnHolder, rankingUpdate); } catch (RemoteException ex) {  Log.e(TAG, "unable to notify listener (posted): " + listener, ex); }}

調(diào)用到最后會執(zhí)行 listener.onNotificationPosted() 方法。通過全局搜索得知,listener 類型是 NotificationListenerService.NotificationListenerWrapper 的代理對象。

NotificationListenerService.NotificationListenerWrapper#onNotificationPosted()

public void onNotificationPosted(IStatusBarNotificationHolder sbnHolder, NotificationRankingUpdate update) { StatusBarNotification sbn; try {  sbn = sbnHolder.get(); } catch (RemoteException e) {  Log.w(TAG, "onNotificationPosted: Error receiving StatusBarNotification", e);  return; } try {  // convert icon metadata to legacy format for older clients  createLegacyIconExtras(sbn.getNotification());  maybePopulateRemoteViews(sbn.getNotification()); } catch (IllegalArgumentException e) {  // warn and drop corrupt notification  Log.w(TAG, "onNotificationPosted: can't rebuild notification from " + sbn.getPackageName());  sbn = null; } // protect subclass from concurrent modifications of (@link mNotificationKeys}. synchronized(mLock) {  applyUpdateLocked(update);  if (sbn != null) {   SomeArgs args = SomeArgs.obtain();   args.arg1 = sbn;   args.arg2 = mRankingMap;   mHandler.obtainMessage(MyHandler.MSG_ON_NOTIFICATION_POSTED, args).sendToTarget();  } else {   // still pass along the ranking map, it may contain other information   mHandler.obtainMessage(MyHandler.MSG_ON_NOTIFICATION_RANKING_UPDATE, mRankingMap).sendToTarget();  } }}

這里在一開始會從 sbnHolder 中獲取到 sbn 對象,sbn 隸屬于 StatusBarNotificationHolder 類,繼承于 IStatusBarNotificationHolder.Stub 對象。注意到這里捕獲了一個 RemoteException,猜測涉及到跨進(jìn)程調(diào)用,但我們不知道這段代碼是在哪個進(jìn)程中執(zhí)行的,所以在這里暫停跟蹤代碼。

筆者之前是通過向系統(tǒng)發(fā)送通知的方式跟蹤源碼,發(fā)現(xiàn)走不通。故個人嘗試從另一個角度入手,即系統(tǒng)接收我們發(fā)過來的通知并顯示到通知欄這個方式入手跟蹤代碼。

系統(tǒng)如何顯示 Notification,即對于系統(tǒng)端來說,Notification 的接收邏輯

系統(tǒng)顯示 Notification 的過程,猜測是在 PhoneStatusBar.java 中,因?yàn)橄到y(tǒng)啟動的過程中,會啟動 SystemUI 進(jìn)程,初始化整個 Android 顯示的界面,包括系統(tǒng)通知欄。

PhoneStatusBar#start()  -> BaseStatusBar#start()

public void start() { // 省略代碼 // Set up the initial notification state. try {  mNotificationListener.registerAsSystemService(mContext,    new ComponentName(mContext.getPackageName(), getClass().getCanonicalName()),    UserHandle.USER_ALL); } catch (RemoteException e) {  Log.e(TAG, "Unable to register notification listener", e); } // 省略代碼}

這段代碼中,會調(diào)用 NotificationListenerService#registerAsSystemService() 方法,涉及到我們之前跟蹤代碼的類。我們繼續(xù)跟進(jìn)去看一下。

NotificationListenerService#registerAsSystemService()

public void registerAsSystemService(Context context, ComponentName componentName,  int currentUser) throws RemoteException { if (mWrapper == null) {  mWrapper = new NotificationListenerWrapper(); } mSystemContext = context; INotificationManager noMan = getNotificationInterface(); mHandler = new MyHandler(context.getMainLooper()); mCurrentUser = currentUser; noMan.registerListener(mWrapper, componentName, currentUser);}

這里會初始化一個 NotificationListenerWrapper 和 mHandler。由于這是在 SystemUI 進(jìn)程中去調(diào)用此方法將 NotificationListenerService 注冊為系統(tǒng)服務(wù),所以在前面分析的那里:

NotificationListenerService.NotificationListenerWrapper#onNotificationPosted(),這段代碼是運(yùn)行在 SystemUI 進(jìn)程,而 mHandler 則是運(yùn)行在 SystemUI 主線程上的 Handler。所以,onNotificationPosted() 是運(yùn)行在 SystemUI 進(jìn)程中,它通過 sbn 從 system_server 進(jìn)程中獲取到 sbn 對象。下一步是通過 mHandler 處理消息,查看 NotificationListenerService.MyHandler#handleMessage() 方法,得知當(dāng) message.what 為 MSG_ON_NOTIFICATION_POSTED 時,調(diào)用的是 onNotificationPosted() 方法。

但是,NotificationListenerService 是一個抽象類,onNotificationPosted() 為空方法,真正的實(shí)現(xiàn)是它的實(shí)例類。

觀察到之前 BaseStatusBar#start() 中,是調(diào)用了 mNotificationListener.registerAsSystemService() 方法。那么,mNotificationListener 是在哪里進(jìn)行初始化呢?

BaseStatusBar.mNotificationListener#onNotificationPosted

private final NotificationListenerService mNotificationListener = new NotificationListenerService() { // 省略代碼  @Override public void onNotificationPosted(final StatusBarNotification sbn, final RankingMap rankingMap) {  if (DEBUG) Log.d(TAG, "onNotificationPosted: " + sbn);  if (sbn != null) {   mHandler.post(new Runnable() {    @Override    public void run() {     processForRemoteInput(sbn.getNotification());     String key = sbn.getKey();     mKeysKeptForRemoteInput.remove(key);     boolean isUpdate = mNotificationData.get(key) != null;     // In case we don't allow child notifications, we ignore children of     // notifications that have a summary, since we're not going to show them     // anyway. This is true also when the summary is canceled,     // because children are automatically canceled by NoMan in that case.     if (!ENABLE_CHILD_NOTIFICATIONS && mGroupManager.isChildInGroupWithSummary(sbn)) {      if (DEBUG) {       Log.d(TAG, "Ignoring group child due to existing summary: " + sbn);      }      // Remove existing notification to avoid stale data.      if (isUpdate) {       removeNotification(key, rankingMap);      } else {       mNotificationData.updateRanking(rankingMap);      }      return;     }     if (isUpdate) {      updateNotification(sbn, rankingMap);     } else {      addNotification(sbn, rankingMap, null /* oldEntry */ );     }    }   });  } } // 省略代碼}

通過上述代碼,我們知道了在 BaseStatusBar.java 中,創(chuàng)建了 NotificationListenerService 的實(shí)例對象,實(shí)現(xiàn)了 onNotificationPost() 這個抽象方法;

在 onNotificationPost() 中,通過 handler 進(jìn)行消息處理,最終調(diào)用 addNotification() 方法

PhoneStatusBar#addNotification()

@Overridepublic void addNotification(StatusBarNotification notification, RankingMap ranking, Entry oldEntry) { if (DEBUG) Log.d(TAG, "addNotification key=" + notification.getKey()); mNotificationData.updateRanking(ranking); Entry shadeEntry = createNotificationViews(notification); if (shadeEntry == null) {  return; } boolean isHeadsUped = shouldPeek(shadeEntry); if (isHeadsUped) {  mHeadsUpManager.showNotification(shadeEntry);  // Mark as seen immediately  setNotificationShown(notification); } if (!isHeadsUped && notification.getNotification().fullScreenIntent != null) {  if (shouldSuppressFullScreenIntent(notification.getKey())) {   if (DEBUG) {    Log.d(TAG, "No Fullscreen intent: suppressed by DND: " + notification.getKey());   }  } else if (mNotificationData.getImportance(notification.getKey()) < NotificationListenerService.Ranking.IMPORTANCE_MAX) {   if (DEBUG) {    Log.d(TAG, "No Fullscreen intent: not important enough: " + notification.getKey());   }  } else {   // Stop screensaver if the notification has a full-screen intent.   // (like an incoming phone call)   awakenDreams();   // not immersive & a full-screen alert should be shown   if (DEBUG) Log.d(TAG, "Notification has fullScreenIntent; sending fullScreenIntent");   try {    EventLog.writeEvent(EventLogTags.SYSUI_FULLSCREEN_NOTIFICATION, notification.getKey());    notification.getNotification().fullScreenIntent.send();    shadeEntry.notifyFullScreenIntentLaunched();    MetricsLogger.count(mContext, "note_fullscreen", 1);   } catch (PendingIntent.CanceledException e) {}  } } // !!! addNotificationViews(shadeEntry, ranking); // Recalculate the position of the sliding windows and the titles. setAreThereNotifications();}

在這個方法中,最關(guān)鍵的方法是最后的 addNotificationViews() 方法。調(diào)用這個方法之后,你創(chuàng)建的 Notification 才會被添加到系統(tǒng)通知欄上。

總結(jié)

跟蹤完整個過程中,之前提到的問題也可以一一解決了:

Q:我們創(chuàng)建的 Notification 實(shí)例最終以什么樣的方式發(fā)送給系統(tǒng)?

A:首先,我們在 app 進(jìn)程創(chuàng)建 Notification 實(shí)例,通過跨進(jìn)程調(diào)用,傳遞到 system_server 進(jìn)程的 NotificationManagerService 中進(jìn)行處理,經(jīng)過兩次異步調(diào)用,最后傳遞給在 NotificationManagerService 中已經(jīng)注冊的 NotificationListenerWrapper。而 android 系統(tǒng)在初始化 systemui 進(jìn)程的時候,會往 NotificationManagerService 中注冊監(jiān)聽器(這里指的就是 NotificationListenerWrapper)。這種實(shí)現(xiàn)方法就是基于我們熟悉的一種設(shè)計(jì)模式:監(jiān)聽者模式。

Q:系統(tǒng)是如何獲取到 Notification 實(shí)例并顯示的?

A:上面提到,由于初始化的時候已經(jīng)往 NotificationManagerService 注冊監(jiān)聽器,所以系統(tǒng) SystemUI 進(jìn)程會接收到 Notification 實(shí)例之后經(jīng)過進(jìn)一步解析,然后構(gòu)造出 Notification Views 并最終顯示在系統(tǒng)通知欄上。

Q:我們是否能攔截 Notification 并獲取其中的信息?

A:通過上面的流程,我個人認(rèn)為可以通過 Xposed 等框架去 hook 其中幾個重要的方法去捕獲 Notification 實(shí)例,例如 hook NotificationManager#notify() 方法去獲取 Notification 實(shí)例。

總結(jié)

以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,如果有疑問大家可以留言交流,謝謝大家對VEVB武林網(wǎng)的支持。


注:相關(guān)教程知識閱讀請移步到Android開發(fā)頻道。
發(fā)表評論 共有條評論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
欧美大交乱xxxxxbbb| 亚洲国产精品成人综合久久久| 久久久久久久久久99| 国产www精品| 国产福利不卡视频| 中文字幕成人乱码在线电影| 人人精品视频| 亚洲一区日本| 国产精品一区在线看| 日韩欧美成人区| 麻豆app在线观看| 亚洲免费观看高清| 欧美激情一区二区久久久| 国产欧美精品日韩区二区麻豆天美| 五月婷婷一区二区三区| 日韩av在线一区二区三区| 国产一区二区激情| a级大胆欧美人体大胆666| 色婷婷视频在线| 色婷婷久久久| 日韩在线观看成人| 亚洲精品电影在线一区| 亚洲深夜福利| 欧美韩日高清| 91小视频xxxx网站在线| 中文字幕在线网站| 国产又粗又黄又猛| 毛片毛片毛片毛| 中文在线免费一区三区高中清不卡| 精品视频久久久久久久| 天堂资源在线亚洲| 久久影院资源网| 欧美一级日本a级v片| 国产又粗又猛又爽又| a级片国产精品自在拍在线播放| 国产91在线免费| 成人免费视频app| wwwwxxxx日韩| 国产精品乱码一区二区三区软件| 黄色av免费播放| 日本精品裸体写真集在线观看| 老女人av在线| 91日韩精品一区| 伊人久久大香线| 亚洲男人的天堂在线aⅴ视频| 亚洲天堂精品视频| 国产精品国产三级国产aⅴ无密码| 午夜激情在线视频| 三级欧美韩日大片在线看| 夜鲁很鲁在线视频| 国产精品久久久久久久免费观看| 亚洲欧洲高清| 日本色综合中文字幕| 欧美精品在线观看播放| av片在线观看网站| 日本视频免费| 日韩av在线网址| 亚洲精品视频免费看| 九色porny蝌蚪视频在线观看| 日韩高清在线一区二区| 红桃视频欧美| 欧美一级爽aaaaa大片| 久久久久久久少妇| www.97av| 欧美精品日韩精品| 一区二区中文字幕在线| 中文 日韩 欧美| 欧美视频在线观看网站| 欧美激情综合亚洲一二区| 欧美大片网站在线观看| 久久精品道一区二区三区| 精品国产18久久久久久二百| 日韩漫画puputoon| 国产成+人+综合+亚洲欧美丁香花| eeuss鲁片一区二区三区| 亚洲精选视频免费看| 国产尤物一区二区| 欧美日韩亚洲第一| 欧美成人黑人猛交| 在线观看免费播放网址成人| 日本久久黄色| 国产精选久久| 国产美女视频一区二区三区| 久久精品影视大全| 日韩精品一二三四区| 色哟哟在线观看一区二区三区| 一级毛片免费视频| 777久久久精品| 亚洲电影成人| 在线观看免费网站| 精品一区二区无码| 一区二区三区视频在线| 日本爱爱小视频| 成年美女黄网站色大片不卡| 亚洲av无码乱码国产麻豆| 天天爱天天做色综合| 亚洲精品aaa| 精品一区二区三区免费视频| 久久久久久久午夜| 亚欧美一区二区三区| 国产精品亚洲午夜一区二区三区| 欧美揉bbbbb揉bbbbb| 中文字幕先锋av影音资源网| 亚洲美女免费精品视频在线观看| www国产黄色| 久久免费视频这里只有精品| youjizz在线视频| 亚洲一区视频| 国产免费一区| 99re8这里有精品热视频8在线| 亚洲私拍自拍| 亚洲精华国产精华精华液网站| 国产乱码一区二区| 成年人在线网站| av影院在线| 欧美绝顶高潮抽搐喷水合集| 国产一区视频在线观看免费| av电影免费在线观看| 日韩三级在线免费观看| 国产午夜视频在线播放| 国产成人在线一区二区| 亚洲国产精品久久人人爱| 欧美性猛交xxxx免费看蜜桃| 五月天丁香社区| 51精品久久久久久久蜜臀| 一区二区视频免费| 国产极品美女在线| 亚洲精品久久区二区三区蜜桃臀| 超级碰碰视频| 午夜激情一区二区三区| 久久久久久噜噜噜久久久精品| 精品久久电影| 国产成人亚洲精品乱码在线观看| 久久众筹精品私拍模特| 天天做天天爱天天爽综合网| 久久综合九色99| 九九爱免费视频在线观看| 色噜噜狠狠色综合欧洲selulu| 成人国内精品久久久久一区| 日韩免费高清av| 国产精品亚洲综合一区在线观看| 99免费视频观看| 国产精品一二三四五区| 国产成人在线观看免费网站| 久久久综合网站| 色老板在线视频| 日韩中文字幕1| 国产精品乱战久久久| 超碰精品在线| 国产日韩一区二区三区| 国产精品激情av电影在线观看| 日韩精品在线观看一区| 国产精彩视频在线观看| 国产精品久久久久久久久久免费| 在线观看成年人视频| 美女日批免费视频| 欧美午夜网站| 欧美特黄一级视频| av中文字幕第一页| 国产suv精品一区二区33| 可以免费看av的网址| 亚洲欧美日本日韩| 澳门av一区二区三区| 国产区在线观看| 国产丝袜视频在线观看| 少妇激情一区二区三区视频| 日本一区二区三区视频| 欧美精品vⅰdeose4hd| a在线观看视频| 国产精品久久久高清免费| 成人高清在线| 欧美激情视频一区二区三区免费| 性一交一乱一伧老太| 国产精品久久久久久av福利软件| 中文字幕日韩精品在线| 91麻豆精品国产91| 久操视频免费在线观看| 欧美激情黄色片| 免费看黄色a级片| 中文字幕免费精品一区高清| 欧美日韩免费不卡视频一区二区三区| 狠狠97人人婷婷五月| 69精品久久久| 国产日韩欧美一区二区三区四区| 亚洲综合在线五月| 色免费在线观看| 日韩av电影免费观看高清完整版| 精品蜜桃一区二区三区| 黄色免费视频| 亚洲国产综合视频在线观看| 中文字幕国产在线观看| 潮喷失禁大喷水aⅴ无码| 91九色porny在线| 色婷婷狠狠18禁久久| 亚洲精品影视在线| 亚洲乱亚洲高清| 91麻豆.com| 欧美人体做爰大胆视频| 亚洲成av人片在线观看香蕉| 欧美久久一区二区| 91无套直看片红桃| 麻豆精品一区二区三区视频| 3d黄动漫网站| 欧美日韩视频免费看| 中文字幕日韩精品在线观看| 欧美激情精品久久久久久小说| 免费视频一区| 曰韩精品一区二区| 国产精品久久久爽爽爽麻豆色哟哟| 欧美成人禁片在线观看网址| 99久久一区三区四区免费| av电影免费在线观看| 免费人成黄页网站在线一区二区| 亚洲欧美综合视频| 欧美日韩一区高清| 国产在线欧美日韩| 日韩成人中文字幕在线观看| 日本v片在线免费观看| 日韩免费久久| 成人不卡视频| 久久精品日韩精品| 欧美专区一二三| 日韩不卡高清| 欧美高清性粉嫩交| 少妇av在线| 手机看片福利盒子久久| 波多野结衣av一区二区全免费观看| 欧美涩涩网站| 国产精品免费一区二区三区四区| 免费在线观看国产黄| 国产精品国语对白| 国产www.大片在线| 欧美激情免费观看| 中文字幕亚洲一区二区三区五十路| 免费一级特黄特色毛片久久看| 自拍偷拍欧美亚洲| 亚洲乱码精品| 欧美网色网址| 婷婷激情四射五月天| 久久久夜色精品| 精品无码m3u8在线观看| 欧美在线国产| 亚洲综合av一区| 亚洲综合色丁香婷婷六月图片| 精品国产一区二区三区在线观看| 欧美精品中文字幕一区| 久久伊人精品一区二区三区| 久久av中文字幕| 中文字幕在线视频播放| 懂色av一区二区三区四区| 亚洲人妻一区二区三区| 日本一区二区三区国色天香| 午夜精品视频一区二区三区在线看| 中文字幕乱码一区二区三区| 5g影院5g天天爽永久免费影院| 99热这里只有精品2| 日韩三级成人av网| 精品magnet| 美女裸体自慰在线观看| 奇米一区二区三区| 懂色aⅴ精品一区二区三区| 中文字幕佐山爱一区二区免费| 亚洲小说欧美另类社区| 成人h小游戏| 国产精品99久久久久久久vr| 青青一区二区| 欧美mv和日韩mv的网站| 国产999精品久久| 欧美日韩国产免费观看| 日韩久久精品一区二区三区| 日韩 国产 在线| 亚洲国产成人一区二区| www.色就是色.com| 欧美日韩在线视频免费观看| 色欲av伊人久久大香线蕉影院| 亚洲第一精品久久忘忧草社区| 欧美,日韩,国产在线| 夜夜嗨aⅴ一区二区三区| 欧美精品免费视频| 欧美精品粉嫩高潮一区二区| 欧美1区免费| 久久亚洲成人精品| 激情六月婷婷综合| 亚洲第一区第二区| mm131美女视频| 亚洲va欧美va国产综合久久| 日韩成人在线视频| 国产手机精品视频| 4438五月综合| 久久全球大尺度高清视频| 青青操在线观看| 亚洲狼人综合干| 国产成人精品电影| 美女做暖暖视频免费在线观看全部网址91| 日韩电影一区二区三区四区| 91免费看片在线观看| caopeng在线| 香蕉成人影院| 香蒸焦蕉伊在线| 欧美激情国产精品免费| 99在线观看| 亚洲视频一区二区在线| 亚洲综合欧美综合| 另类小说综合欧美亚洲| 在线中文字日产幕| 欧美成人精品一区二区| 暗呦丨小u女国产精品| √资源天堂中文在线| 超碰av在线免费观看| 日韩视频欧美视频| 国产按摩一区二区三区| 亚洲av成人片色在线观看高潮| 久久免费精彩视频| 国产ts人妖调教重口男| 麻豆精品国产91久久久久久| 日av在线播放| 91亚洲一区精品| 国产精品免费一区二区| 日本黄色精品| 1区2区3区在线| 99亚洲精品| 亚洲成人午夜在线| 国产成人免费9x9x人网站视频| 成人乱码一区二区三区| 一级特黄a大片免费| 无遮挡又爽又刺激的视频| 日产精品久久久一区二区| 一区二区国产欧美|